Mercurial > pub > bltoolkit
comparison Source/Data/Linq/Query.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children | f757da6161a1 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f990fcb411a9 |
---|---|
1 using System; | |
2 using System.Collections; | |
3 using System.Collections.Generic; | |
4 using System.Data; | |
5 using System.Linq; | |
6 using System.Linq.Expressions; | |
7 | |
8 namespace BLToolkit.Data.Linq | |
9 { | |
10 using BLToolkit.Linq; | |
11 using Common; | |
12 using Data.Sql; | |
13 using Data.Sql.SqlProvider; | |
14 using Mapping; | |
15 using Builder; | |
16 using Reflection; | |
17 | |
18 public abstract class Query | |
19 { | |
20 #region Init | |
21 | |
22 public abstract void Init(IBuildContext parseContext, List<ParameterAccessor> sqlParameters); | |
23 | |
24 #endregion | |
25 | |
26 #region Compare | |
27 | |
28 public string ContextID; | |
29 public Expression Expression; | |
30 public MappingSchema MappingSchema; | |
31 | |
32 public bool Compare(string contextID, MappingSchema mappingSchema, Expression expr) | |
33 { | |
34 return | |
35 ContextID.Length == contextID.Length && | |
36 ContextID == contextID && | |
37 MappingSchema == mappingSchema && | |
38 ExpressionHelper.Compare(Expression, expr, _queryableAccessorDic); | |
39 } | |
40 | |
41 readonly Dictionary<Expression,QueryableAccessor> _queryableAccessorDic = new Dictionary<Expression,QueryableAccessor>(); | |
42 readonly List<QueryableAccessor> _queryableAccessorList = new List<QueryableAccessor>(); | |
43 | |
44 internal int AddQueryableAccessors(Expression expr, Expression<Func<Expression,IQueryable>> qe) | |
45 { | |
46 QueryableAccessor e; | |
47 | |
48 if (_queryableAccessorDic.TryGetValue(expr, out e)) | |
49 return _queryableAccessorList.IndexOf(e); | |
50 | |
51 e = new QueryableAccessor { Accessor = qe.Compile() }; | |
52 e.Queryable = e.Accessor(expr); | |
53 | |
54 _queryableAccessorDic. Add(expr, e); | |
55 _queryableAccessorList.Add(e); | |
56 | |
57 return _queryableAccessorList.Count - 1; | |
58 } | |
59 | |
60 public Expression GetIQueryable(int n, Expression expr) | |
61 { | |
62 return _queryableAccessorList[n].Accessor(expr).Expression; | |
63 } | |
64 | |
65 #endregion | |
66 } | |
67 | |
68 public class Query<T> : Query | |
69 { | |
70 public Query() | |
71 { | |
72 GetIEnumerable = MakeEnumerable; | |
73 } | |
74 | |
75 public override void Init(IBuildContext parseContext, List<ParameterAccessor> sqlParameters) | |
76 { | |
77 Queries.Add(new QueryInfo | |
78 { | |
79 SqlQuery = parseContext.SqlQuery, | |
80 Parameters = sqlParameters, | |
81 }); | |
82 | |
83 ContextID = parseContext.Builder.DataContextInfo.ContextID; | |
84 MappingSchema = parseContext.Builder.MappingSchema; | |
85 CreateSqlProvider = parseContext.Builder.DataContextInfo.CreateSqlProvider; | |
86 Expression = parseContext.Builder.OriginalExpression; | |
87 //Parameters = parameters; | |
88 } | |
89 | |
90 #region Properties & Fields | |
91 | |
92 public Query<T> Next; | |
93 public ParameterExpression[] CompiledParameters; | |
94 public List<QueryInfo> Queries = new List<QueryInfo>(1); | |
95 public Func<ISqlProvider> CreateSqlProvider; | |
96 | |
97 private ISqlProvider _sqlProvider; | |
98 public ISqlProvider SqlProvider | |
99 { | |
100 get { return _sqlProvider ?? (_sqlProvider = CreateSqlProvider()); } | |
101 } | |
102 | |
103 public Func<QueryContext,IDataContextInfo,Expression,object[],object> GetElement; | |
104 public Func<QueryContext,IDataContextInfo,Expression,object[],IEnumerable<T>> GetIEnumerable; | |
105 | |
106 IEnumerable<T> MakeEnumerable(QueryContext qc, IDataContextInfo dci, Expression expr, object[] ps) | |
107 { | |
108 yield return ConvertTo<T>.From(GetElement(qc, dci, expr, ps)); | |
109 } | |
110 | |
111 #endregion | |
112 | |
113 #region GetInfo | |
114 | |
115 static Query<T> _first; | |
116 static readonly object _sync = new object(); | |
117 | |
118 const int CacheSize = 100; | |
119 | |
120 public static Query<T> GetQuery(IDataContextInfo dataContextInfo, Expression expr) | |
121 { | |
122 var query = FindQuery(dataContextInfo, expr); | |
123 | |
124 if (query == null) | |
125 { | |
126 lock (_sync) | |
127 { | |
128 query = FindQuery(dataContextInfo, expr); | |
129 | |
130 if (query == null) | |
131 { | |
132 if (Configuration.Linq.GenerateExpressionTest) | |
133 { | |
134 #if FW4 || SILVERLIGHT | |
135 var testFile = new ExpressionTestGenerator().GenerateSource(expr); | |
136 #else | |
137 var testFile = ""; | |
138 #endif | |
139 | |
140 #if !SILVERLIGHT | |
141 DbManager.WriteTraceLine( | |
142 "Expression test code generated: '" + testFile + "'.", | |
143 DbManager.TraceSwitch.DisplayName); | |
144 #endif | |
145 } | |
146 | |
147 try | |
148 { | |
149 query = new ExpressionBuilder(new Query<T>(), dataContextInfo, expr, null).Build<T>(); | |
150 } | |
151 catch (Exception) | |
152 { | |
153 if (!Configuration.Linq.GenerateExpressionTest) | |
154 { | |
155 #if !SILVERLIGHT | |
156 DbManager.WriteTraceLine( | |
157 "To generate test code to diagnose the problem set 'BLToolkit.Common.Configuration.Linq.GenerateExpressionTest = true'.", | |
158 DbManager.TraceSwitch.DisplayName); | |
159 #endif | |
160 } | |
161 | |
162 throw; | |
163 } | |
164 | |
165 query.Next = _first; | |
166 _first = query; | |
167 } | |
168 } | |
169 } | |
170 | |
171 return query; | |
172 } | |
173 | |
174 static Query<T> FindQuery(IDataContextInfo dataContextInfo, Expression expr) | |
175 { | |
176 Query<T> prev = null; | |
177 var n = 0; | |
178 | |
179 for (var query = _first; query != null; query = query.Next) | |
180 { | |
181 if (query.Compare(dataContextInfo.ContextID, dataContextInfo.MappingSchema, expr)) | |
182 { | |
183 if (prev != null) | |
184 { | |
185 lock (_sync) | |
186 { | |
187 prev.Next = query.Next; | |
188 query.Next = _first; | |
189 _first = query; | |
190 } | |
191 } | |
192 | |
193 return query; | |
194 } | |
195 | |
196 if (n++ >= CacheSize) | |
197 { | |
198 query.Next = null; | |
199 return null; | |
200 } | |
201 | |
202 prev = query; | |
203 } | |
204 | |
205 return null; | |
206 } | |
207 | |
208 #endregion | |
209 | |
210 #region NonQueryQuery | |
211 | |
212 void FinalizeQuery() | |
213 { | |
214 foreach (var sql in Queries) | |
215 { | |
216 sql.SqlQuery = SqlProvider.Finalize(sql.SqlQuery); | |
217 sql.Parameters = sql.Parameters | |
218 .Select (p => new { p, idx = sql.SqlQuery.Parameters.IndexOf(p.SqlParameter) }) | |
219 .OrderBy(p => p.idx) | |
220 .Select (p => p.p) | |
221 .ToList(); | |
222 } | |
223 } | |
224 | |
225 public void SetNonQueryQuery() | |
226 { | |
227 FinalizeQuery(); | |
228 | |
229 if (Queries.Count != 1) | |
230 throw new InvalidOperationException(); | |
231 | |
232 SqlProvider.SqlQuery = Queries[0].SqlQuery; | |
233 | |
234 GetElement = (ctx,db,expr,ps) => NonQueryQuery(db, expr, ps); | |
235 } | |
236 | |
237 int NonQueryQuery(IDataContextInfo dataContextInfo, Expression expr, object[] parameters) | |
238 { | |
239 var dataContext = dataContextInfo.DataContext; | |
240 | |
241 object query = null; | |
242 | |
243 try | |
244 { | |
245 query = SetCommand(dataContext, expr, parameters, 0); | |
246 return dataContext.ExecuteNonQuery(query); | |
247 } | |
248 finally | |
249 { | |
250 if (query != null) | |
251 dataContext.ReleaseQuery(query); | |
252 | |
253 if (dataContextInfo.DisposeContext) | |
254 dataContext.Dispose(); | |
255 } | |
256 } | |
257 | |
258 public void SetNonQueryQuery2() | |
259 { | |
260 FinalizeQuery(); | |
261 | |
262 if (Queries.Count != 2) | |
263 throw new InvalidOperationException(); | |
264 | |
265 SqlProvider.SqlQuery = Queries[0].SqlQuery; | |
266 | |
267 GetElement = (ctx,db,expr,ps) => NonQueryQuery2(db, expr, ps); | |
268 } | |
269 | |
270 int NonQueryQuery2(IDataContextInfo dataContextInfo, Expression expr, object[] parameters) | |
271 { | |
272 var dataContext = dataContextInfo.DataContext; | |
273 | |
274 object query = null; | |
275 | |
276 try | |
277 { | |
278 query = SetCommand(dataContext, expr, parameters, 0); | |
279 | |
280 var n = dataContext.ExecuteNonQuery(query); | |
281 | |
282 if (n != 0) | |
283 return n; | |
284 | |
285 query = SetCommand(dataContext, expr, parameters, 1); | |
286 return dataContext.ExecuteNonQuery(query); | |
287 } | |
288 finally | |
289 { | |
290 if (query != null) | |
291 dataContext.ReleaseQuery(query); | |
292 | |
293 if (dataContextInfo.DisposeContext) | |
294 dataContext.Dispose(); | |
295 } | |
296 } | |
297 | |
298 #endregion | |
299 | |
300 #region ScalarQuery | |
301 | |
302 public void SetScalarQuery<TS>() | |
303 { | |
304 FinalizeQuery(); | |
305 | |
306 if (Queries.Count != 1) | |
307 throw new InvalidOperationException(); | |
308 | |
309 SqlProvider.SqlQuery = Queries[0].SqlQuery; | |
310 | |
311 GetElement = (ctx,db,expr,ps) => ScalarQuery<TS>(db, expr, ps); | |
312 } | |
313 | |
314 TS ScalarQuery<TS>(IDataContextInfo dataContextInfo, Expression expr, object[] parameters) | |
315 { | |
316 var dataContext = dataContextInfo.DataContext; | |
317 | |
318 object query = null; | |
319 | |
320 try | |
321 { | |
322 query = SetCommand(dataContext, expr, parameters, 0); | |
323 return (TS)dataContext.ExecuteScalar(query); | |
324 } | |
325 finally | |
326 { | |
327 if (query != null) | |
328 dataContext.ReleaseQuery(query); | |
329 | |
330 if (dataContextInfo.DisposeContext) | |
331 dataContext.Dispose(); | |
332 } | |
333 } | |
334 | |
335 #endregion | |
336 | |
337 #region Query | |
338 | |
339 int GetParameterIndex(ISqlExpression parameter) | |
340 { | |
341 for (var i = 0; i < Queries[0].Parameters.Count; i++) | |
342 { | |
343 var p = Queries[0].Parameters[i].SqlParameter; | |
344 | |
345 if (p == parameter) | |
346 return i; | |
347 } | |
348 | |
349 throw new InvalidOperationException(); | |
350 } | |
351 | |
352 IEnumerable<IDataReader> RunQuery(IDataContextInfo dataContextInfo, Expression expr, object[] parameters, int queryNumber) | |
353 { | |
354 var dataContext = dataContextInfo.DataContext; | |
355 | |
356 object query = null; | |
357 | |
358 try | |
359 { | |
360 query = SetCommand(dataContext, expr, parameters, queryNumber); | |
361 | |
362 using (var dr = dataContext.ExecuteReader(query)) | |
363 while (dr.Read()) | |
364 yield return dr; | |
365 } | |
366 finally | |
367 { | |
368 if (query != null) | |
369 dataContext.ReleaseQuery(query); | |
370 | |
371 if (dataContextInfo.DisposeContext) | |
372 dataContext.Dispose(); | |
373 } | |
374 } | |
375 | |
376 object SetCommand(IDataContext dataContext, Expression expr, object[] parameters, int idx) | |
377 { | |
378 lock (this) | |
379 { | |
380 SetParameters(expr, parameters, idx); | |
381 return dataContext.SetQuery(Queries[idx]); | |
382 } | |
383 } | |
384 | |
385 void SetParameters(Expression expr, object[] parameters, int idx) | |
386 { | |
387 foreach (var p in Queries[idx].Parameters) | |
388 { | |
389 var value = p.Accessor(expr, parameters); | |
390 | |
391 if (value is IEnumerable) | |
392 { | |
393 var type = value.GetType(); | |
394 var etype = TypeHelper.GetElementType(type); | |
395 | |
396 if (etype == null || etype == typeof(object) || | |
397 etype.IsEnum || | |
398 (TypeHelper.IsNullableType(etype) && etype.GetGenericArguments()[0].IsEnum)) | |
399 { | |
400 var values = new List<object>(); | |
401 | |
402 foreach (var v in (IEnumerable)value) | |
403 { | |
404 values.Add(v); | |
405 // Enum mapping done by parameter itself | |
406 //values.Add(v != null && v.GetType().IsEnum ? | |
407 // MappingSchema.MapEnumToValue(v, true) : | |
408 // v); | |
409 } | |
410 | |
411 value = values; | |
412 } | |
413 } | |
414 | |
415 p.SqlParameter.Value = value; | |
416 } | |
417 } | |
418 | |
419 #endregion | |
420 | |
421 #region GetSqlText | |
422 | |
423 public string GetSqlText(IDataContext dataContext, Expression expr, object[] parameters, int idx) | |
424 { | |
425 var query = SetCommand(dataContext, expr, parameters, 0); | |
426 return dataContext.GetSqlText(query); | |
427 } | |
428 | |
429 #endregion | |
430 | |
431 #region Inner Types | |
432 | |
433 internal delegate TElement Mapper<TElement>( | |
434 Query<T> query, | |
435 QueryContext qc, | |
436 IDataContext dc, | |
437 IDataReader rd, | |
438 MappingSchema ms, | |
439 Expression expr, | |
440 object[] ps); | |
441 | |
442 public class QueryInfo : IQueryContext | |
443 { | |
444 public QueryInfo() | |
445 { | |
446 SqlQuery = new SqlQuery(); | |
447 } | |
448 | |
449 public SqlQuery SqlQuery { get; set; } | |
450 public object Context { get; set; } | |
451 | |
452 public SqlParameter[] GetParameters() | |
453 { | |
454 var ps = new SqlParameter[Parameters.Count]; | |
455 | |
456 for (var i = 0; i < ps.Length; i++) | |
457 ps[i] = Parameters[i].SqlParameter; | |
458 | |
459 return ps; | |
460 } | |
461 | |
462 public List<ParameterAccessor> Parameters = new List<ParameterAccessor>(); | |
463 } | |
464 | |
465 #endregion | |
466 | |
467 #region Object Operations | |
468 | |
469 static class ObjectOperation<T1> | |
470 { | |
471 public static readonly Dictionary<object,Query<int>> Insert = new Dictionary<object,Query<int>>(); | |
472 public static readonly Dictionary<object,Query<object>> InsertWithIdentity = new Dictionary<object,Query<object>>(); | |
473 public static readonly Dictionary<object,Query<int>> InsertOrUpdate = new Dictionary<object,Query<int>>(); | |
474 public static readonly Dictionary<object,Query<int>> Update = new Dictionary<object,Query<int>>(); | |
475 public static readonly Dictionary<object,Query<int>> Delete = new Dictionary<object,Query<int>>(); | |
476 } | |
477 | |
478 static object ConvertNullable<TT>(TT value, TT defaultValue) | |
479 where TT : struct | |
480 { | |
481 return value.Equals(defaultValue) ? null : (object)value; | |
482 } | |
483 | |
484 static ParameterAccessor GetParameter<TR>(IDataContext dataContext, SqlField field) | |
485 { | |
486 var exprParam = Expression.Parameter(typeof(Expression), "expr"); | |
487 | |
488 Expression getter = Expression.Convert( | |
489 Expression.Property( | |
490 Expression.Convert(exprParam, typeof(ConstantExpression)), | |
491 ReflectionHelper.Constant.Value), | |
492 typeof(T)); | |
493 | |
494 var mm = field.MemberMapper; | |
495 var members = mm.MemberName.Split('.'); | |
496 var defValue = Expression.Constant( | |
497 mm.MapMemberInfo.DefaultValue ?? TypeHelper.GetDefaultValue(mm.MapMemberInfo.Type), | |
498 mm.MapMemberInfo.Type); | |
499 | |
500 for (var i = 0; i < members.Length; i++) | |
501 { | |
502 var member = members[i]; | |
503 var pof = Expression.PropertyOrField(getter, member) as Expression; | |
504 | |
505 if (i == 0) | |
506 { | |
507 if (members.Length == 1 && mm.IsExplicit) | |
508 { | |
509 if (getter.Type != typeof(object)) | |
510 getter = Expression.Convert(getter, typeof(object)); | |
511 | |
512 pof = Expression.Call( | |
513 Expression.Constant(mm), | |
514 ReflectionHelper.Expressor<MemberMapper>.MethodExpressor(m => m.GetValue(null)), | |
515 getter); | |
516 } | |
517 | |
518 getter = pof; | |
519 } | |
520 else | |
521 { | |
522 getter = Expression.Condition(Expression.Equal(getter, Expression.Constant(null)), defValue, pof); | |
523 } | |
524 } | |
525 | |
526 if (!mm.Type.IsClass && !mm.Type.IsInterface && mm.MapMemberInfo.Nullable && !TypeHelper.IsNullableType(mm.Type)) | |
527 { | |
528 var method = ReflectionHelper.Expressor<int>.MethodExpressor(_ => ConvertNullable(0, 0)) | |
529 .GetGenericMethodDefinition() | |
530 .MakeGenericMethod(mm.Type); | |
531 | |
532 getter = Expression.Call(null, method, getter, Expression.Constant(mm.MapMemberInfo.NullValue)); | |
533 } | |
534 else | |
535 { | |
536 if (getter.Type != typeof(object)) | |
537 getter = Expression.Convert(getter, typeof(object)); | |
538 } | |
539 | |
540 var mapper = Expression.Lambda<Func<Expression,object[],object>>( | |
541 getter, | |
542 new [] { exprParam, Expression.Parameter(typeof(object[]), "ps") }); | |
543 | |
544 var param = new ParameterAccessor | |
545 { | |
546 Expression = null, | |
547 Accessor = mapper.Compile(), | |
548 SqlParameter = new SqlParameter(field.SystemType, field.Name.Replace('.', '_'), null, dataContext.MappingSchema) | |
549 }; | |
550 | |
551 if (TypeHelper.IsEnumOrNullableEnum(field.SystemType)) | |
552 { | |
553 param.SqlParameter.SetEnumConverter(field.MemberMapper.ComplexMemberAccessor, dataContext.MappingSchema); | |
554 } | |
555 | |
556 return param; | |
557 } | |
558 | |
559 #region Insert | |
560 | |
561 public static int Insert(IDataContextInfo dataContextInfo, T obj) | |
562 { | |
563 if (Equals(default(T), obj)) | |
564 return 0; | |
565 | |
566 Query<int> ei; | |
567 | |
568 var key = new { dataContextInfo.MappingSchema, dataContextInfo.ContextID }; | |
569 | |
570 if (!ObjectOperation<T>.Insert.TryGetValue(key, out ei)) | |
571 lock (_sync) | |
572 if (!ObjectOperation<T>.Insert.TryGetValue(key, out ei)) | |
573 { | |
574 var sqlTable = new SqlTable<T>(dataContextInfo.MappingSchema); | |
575 var sqlQuery = new SqlQuery { QueryType = QueryType.Insert }; | |
576 | |
577 sqlQuery.Insert.Into = sqlTable; | |
578 | |
579 ei = new Query<int> | |
580 { | |
581 MappingSchema = dataContextInfo.MappingSchema, | |
582 ContextID = dataContextInfo.ContextID, | |
583 CreateSqlProvider = dataContextInfo.CreateSqlProvider, | |
584 Queries = { new Query<int>.QueryInfo { SqlQuery = sqlQuery, } } | |
585 }; | |
586 | |
587 foreach (var field in sqlTable.Fields) | |
588 { | |
589 if (field.Value.IsInsertable) | |
590 { | |
591 var param = GetParameter<int>(dataContextInfo.DataContext, field.Value); | |
592 | |
593 ei.Queries[0].Parameters.Add(param); | |
594 | |
595 sqlQuery.Insert.Items.Add(new SqlQuery.SetExpression(field.Value, param.SqlParameter)); | |
596 } | |
597 else if (field.Value.IsIdentity) | |
598 { | |
599 var expr = ei.SqlProvider.GetIdentityExpression(sqlTable, field.Value, false); | |
600 | |
601 if (expr != null) | |
602 sqlQuery.Insert.Items.Add(new SqlQuery.SetExpression(field.Value, expr)); | |
603 } | |
604 } | |
605 | |
606 ei.SetNonQueryQuery(); | |
607 | |
608 ObjectOperation<T>.Insert.Add(key, ei); | |
609 } | |
610 | |
611 return (int)ei.GetElement(null, dataContextInfo, Expression.Constant(obj), null); | |
612 } | |
613 | |
614 #endregion | |
615 | |
616 #region InsertWithIdentity | |
617 | |
618 public static object InsertWithIdentity(IDataContextInfo dataContextInfo, T obj) | |
619 { | |
620 if (Equals(default(T), obj)) | |
621 return 0; | |
622 | |
623 Query<object> ei; | |
624 | |
625 var key = new { dataContextInfo.MappingSchema, dataContextInfo.ContextID }; | |
626 | |
627 if (!ObjectOperation<T>.InsertWithIdentity.TryGetValue(key, out ei)) | |
628 lock (_sync) | |
629 if (!ObjectOperation<T>.InsertWithIdentity.TryGetValue(key, out ei)) | |
630 { | |
631 var sqlTable = new SqlTable<T>(dataContextInfo.MappingSchema); | |
632 var sqlQuery = new SqlQuery { QueryType = QueryType.Insert }; | |
633 | |
634 sqlQuery.Insert.Into = sqlTable; | |
635 sqlQuery.Insert.WithIdentity = true; | |
636 | |
637 ei = new Query<object> | |
638 { | |
639 MappingSchema = dataContextInfo.MappingSchema, | |
640 ContextID = dataContextInfo.ContextID, | |
641 CreateSqlProvider = dataContextInfo.CreateSqlProvider, | |
642 Queries = { new Query<object>.QueryInfo { SqlQuery = sqlQuery, } } | |
643 }; | |
644 | |
645 foreach (var field in sqlTable.Fields) | |
646 { | |
647 if (field.Value.IsInsertable) | |
648 { | |
649 var param = GetParameter<object>(dataContextInfo.DataContext, field.Value); | |
650 | |
651 ei.Queries[0].Parameters.Add(param); | |
652 | |
653 sqlQuery.Insert.Items.Add(new SqlQuery.SetExpression(field.Value, param.SqlParameter)); | |
654 } | |
655 else if (field.Value.IsIdentity) | |
656 { | |
657 var expr = ei.SqlProvider.GetIdentityExpression(sqlTable, field.Value, true); | |
658 | |
659 if (expr != null) | |
660 sqlQuery.Insert.Items.Add(new SqlQuery.SetExpression(field.Value, expr)); | |
661 } | |
662 } | |
663 | |
664 ei.SetScalarQuery<object>(); | |
665 | |
666 ObjectOperation<T>.InsertWithIdentity.Add(key, ei); | |
667 } | |
668 | |
669 return ei.GetElement(null, dataContextInfo, Expression.Constant(obj), null); | |
670 } | |
671 | |
672 #endregion | |
673 | |
674 #region InsertOrReplace | |
675 | |
676 [Obsolete("Use 'InsertOrReplace' instead.")] | |
677 public static int InsertOrUpdate(IDataContextInfo dataContextInfo, T obj) | |
678 { | |
679 return InsertOrReplace(dataContextInfo, obj); | |
680 } | |
681 | |
682 public static int InsertOrReplace(IDataContextInfo dataContextInfo, T obj) | |
683 { | |
684 if (Equals(default(T), obj)) | |
685 return 0; | |
686 | |
687 Query<int> ei; | |
688 | |
689 var key = new { dataContextInfo.MappingSchema, dataContextInfo.ContextID }; | |
690 | |
691 if (!ObjectOperation<T>.InsertOrUpdate.TryGetValue(key, out ei)) | |
692 { | |
693 lock (_sync) | |
694 { | |
695 if (!ObjectOperation<T>.InsertOrUpdate.TryGetValue(key, out ei)) | |
696 { | |
697 var fieldDic = new Dictionary<SqlField, ParameterAccessor>(); | |
698 var sqlTable = new SqlTable<T>(dataContextInfo.MappingSchema); | |
699 var sqlQuery = new SqlQuery { QueryType = QueryType.InsertOrUpdate }; | |
700 | |
701 ParameterAccessor param; | |
702 | |
703 sqlQuery.Insert.Into = sqlTable; | |
704 sqlQuery.Update.Table = sqlTable; | |
705 | |
706 sqlQuery.From.Table(sqlTable); | |
707 | |
708 ei = new Query<int> | |
709 { | |
710 MappingSchema = dataContextInfo.MappingSchema, | |
711 ContextID = dataContextInfo.ContextID, | |
712 CreateSqlProvider = dataContextInfo.CreateSqlProvider, | |
713 Queries = { new Query<int>.QueryInfo { SqlQuery = sqlQuery, } } | |
714 }; | |
715 | |
716 var supported = ei.SqlProvider.IsInsertOrUpdateSupported && ei.SqlProvider.CanCombineParameters; | |
717 | |
718 // Insert. | |
719 // | |
720 foreach (var field in sqlTable.Fields.Select(f => f.Value)) | |
721 { | |
722 if (field.IsInsertable) | |
723 { | |
724 if (!supported || !fieldDic.TryGetValue(field, out param)) | |
725 { | |
726 param = GetParameter<int>(dataContextInfo.DataContext, field); | |
727 ei.Queries[0].Parameters.Add(param); | |
728 | |
729 if (supported) | |
730 fieldDic.Add(field, param); | |
731 } | |
732 | |
733 sqlQuery.Insert.Items.Add(new SqlQuery.SetExpression(field, param.SqlParameter)); | |
734 } | |
735 else if (field.IsIdentity) | |
736 { | |
737 throw new LinqException("InsertOrUpdate method does not support identity field '{0}.{1}'.", sqlTable.Name, field.Name); | |
738 } | |
739 } | |
740 | |
741 // Update. | |
742 // | |
743 var keys = sqlTable.GetKeys(true).Cast<SqlField>().ToList(); | |
744 var fields = sqlTable.Fields.Values.Where(f => f.IsUpdatable).Except(keys).ToList(); | |
745 | |
746 if (keys.Count == 0) | |
747 throw new LinqException("InsertOrUpdate method requires the '{0}' table to have a primary key.", sqlTable.Name); | |
748 | |
749 var q = | |
750 ( | |
751 from k in keys | |
752 join i in sqlQuery.Insert.Items on k equals i.Column | |
753 select new { k, i } | |
754 ).ToList(); | |
755 | |
756 var missedKey = keys.Except(q.Select(i => i.k)).FirstOrDefault(); | |
757 | |
758 if (missedKey != null) | |
759 throw new LinqException("InsertOrUpdate method requires the '{0}.{1}' field to be included in the insert setter.", | |
760 sqlTable.Name, | |
761 missedKey.Name); | |
762 | |
763 if (fields.Count == 0) | |
764 throw new LinqException( | |
765 string.Format("There are no fields to update in the type '{0}'.", sqlTable.Name)); | |
766 | |
767 foreach (var field in fields) | |
768 { | |
769 if (!supported || !fieldDic.TryGetValue(field, out param)) | |
770 { | |
771 param = GetParameter<int>(dataContextInfo.DataContext, field); | |
772 ei.Queries[0].Parameters.Add(param); | |
773 | |
774 if (supported) | |
775 fieldDic.Add(field, param = GetParameter<int>(dataContextInfo.DataContext, field)); | |
776 } | |
777 | |
778 sqlQuery.Update.Items.Add(new SqlQuery.SetExpression(field, param.SqlParameter)); | |
779 } | |
780 | |
781 sqlQuery.Update.Keys.AddRange(q.Select(i => i.i)); | |
782 | |
783 // Set the query. | |
784 // | |
785 if (ei.SqlProvider.IsInsertOrUpdateSupported) | |
786 ei.SetNonQueryQuery(); | |
787 else | |
788 ei.MakeAlternativeInsertOrUpdate(sqlQuery); | |
789 | |
790 ObjectOperation<T>.InsertOrUpdate.Add(key, ei); | |
791 } | |
792 } | |
793 } | |
794 | |
795 return (int)ei.GetElement(null, dataContextInfo, Expression.Constant(obj), null); | |
796 } | |
797 | |
798 internal void MakeAlternativeInsertOrUpdate(SqlQuery sqlQuery) | |
799 { | |
800 var dic = new Dictionary<ICloneableElement,ICloneableElement>(); | |
801 | |
802 var insertQuery = (SqlQuery)sqlQuery.Clone(dic, _ => true); | |
803 | |
804 insertQuery.QueryType = QueryType.Insert; | |
805 insertQuery.ClearUpdate(); | |
806 insertQuery.From.Tables.Clear(); | |
807 | |
808 Queries.Add(new QueryInfo | |
809 { | |
810 SqlQuery = insertQuery, | |
811 Parameters = Queries[0].Parameters | |
812 .Select(p => new ParameterAccessor | |
813 { | |
814 Expression = p.Expression, | |
815 Accessor = p.Accessor, | |
816 SqlParameter = dic.ContainsKey(p.SqlParameter) ? (SqlParameter)dic[p.SqlParameter] : null | |
817 }) | |
818 .Where(p => p.SqlParameter != null) | |
819 .ToList(), | |
820 }); | |
821 | |
822 var keys = sqlQuery.Update.Keys; | |
823 | |
824 foreach (var key in keys) | |
825 sqlQuery.Where.Expr(key.Column).Equal.Expr(key.Expression); | |
826 | |
827 sqlQuery.QueryType = QueryType.Update; | |
828 sqlQuery.ClearInsert(); | |
829 | |
830 SetNonQueryQuery2(); | |
831 | |
832 Queries.Add(new QueryInfo | |
833 { | |
834 SqlQuery = insertQuery, | |
835 Parameters = Queries[0].Parameters.ToList(), | |
836 }); | |
837 } | |
838 | |
839 #endregion | |
840 | |
841 #region Update | |
842 | |
843 public static int Update(IDataContextInfo dataContextInfo, T obj) | |
844 { | |
845 if (Equals(default(T), obj)) | |
846 return 0; | |
847 | |
848 Query<int> ei; | |
849 | |
850 var key = new { dataContextInfo.MappingSchema, dataContextInfo.ContextID }; | |
851 | |
852 if (!ObjectOperation<T>.Update.TryGetValue(key, out ei)) | |
853 lock (_sync) | |
854 if (!ObjectOperation<T>.Update.TryGetValue(key, out ei)) | |
855 { | |
856 var sqlTable = new SqlTable<T>(dataContextInfo.MappingSchema); | |
857 var sqlQuery = new SqlQuery { QueryType = QueryType.Update }; | |
858 | |
859 sqlQuery.From.Table(sqlTable); | |
860 | |
861 ei = new Query<int> | |
862 { | |
863 MappingSchema = dataContextInfo.MappingSchema, | |
864 ContextID = dataContextInfo.ContextID, | |
865 CreateSqlProvider = dataContextInfo.CreateSqlProvider, | |
866 Queries = { new Query<int>.QueryInfo { SqlQuery = sqlQuery, } } | |
867 }; | |
868 | |
869 var keys = sqlTable.GetKeys(true).Cast<SqlField>(); | |
870 var fields = sqlTable.Fields.Values.Where(f => f.IsUpdatable).Except(keys).ToList(); | |
871 | |
872 if (fields.Count == 0) | |
873 { | |
874 if (Configuration.Linq.IgnoreEmptyUpdate) | |
875 return 0; | |
876 | |
877 throw new LinqException( | |
878 string.Format("There are no fields to update in the type '{0}'.", sqlTable.Name)); | |
879 } | |
880 | |
881 foreach (var field in fields) | |
882 { | |
883 var param = GetParameter<int>(dataContextInfo.DataContext, field); | |
884 | |
885 ei.Queries[0].Parameters.Add(param); | |
886 | |
887 sqlQuery.Update.Items.Add(new SqlQuery.SetExpression(field, param.SqlParameter)); | |
888 } | |
889 | |
890 foreach (var field in keys) | |
891 { | |
892 var param = GetParameter<int>(dataContextInfo.DataContext, field); | |
893 | |
894 ei.Queries[0].Parameters.Add(param); | |
895 | |
896 sqlQuery.Where.Field(field).Equal.Expr(param.SqlParameter); | |
897 | |
898 if (field.Nullable) | |
899 sqlQuery.IsParameterDependent = true; | |
900 } | |
901 | |
902 ei.SetNonQueryQuery(); | |
903 | |
904 ObjectOperation<T>.Update.Add(key, ei); | |
905 } | |
906 | |
907 return (int)ei.GetElement(null, dataContextInfo, Expression.Constant(obj), null); | |
908 } | |
909 | |
910 #endregion | |
911 | |
912 #region Delete | |
913 | |
914 public static int Delete(IDataContextInfo dataContextInfo, T obj) | |
915 { | |
916 if (Equals(default(T), obj)) | |
917 return 0; | |
918 | |
919 Query<int> ei; | |
920 | |
921 var key = new { dataContextInfo.MappingSchema, dataContextInfo.ContextID }; | |
922 | |
923 if (!ObjectOperation<T>.Delete.TryGetValue(key, out ei)) | |
924 lock (_sync) | |
925 if (!ObjectOperation<T>.Delete.TryGetValue(key, out ei)) | |
926 { | |
927 var sqlTable = new SqlTable<T>(dataContextInfo.MappingSchema); | |
928 var sqlQuery = new SqlQuery { QueryType = QueryType.Delete }; | |
929 | |
930 sqlQuery.From.Table(sqlTable); | |
931 | |
932 ei = new Query<int> | |
933 { | |
934 MappingSchema = dataContextInfo.MappingSchema, | |
935 ContextID = dataContextInfo.ContextID, | |
936 CreateSqlProvider = dataContextInfo.CreateSqlProvider, | |
937 Queries = { new Query<int>.QueryInfo { SqlQuery = sqlQuery, } } | |
938 }; | |
939 | |
940 var keys = sqlTable.GetKeys(true).Cast<SqlField>().ToList(); | |
941 | |
942 if (keys.Count == 0) | |
943 throw new LinqException( | |
944 string.Format("Table '{0}' does not have primary key.", sqlTable.Name)); | |
945 | |
946 foreach (var field in keys) | |
947 { | |
948 var param = GetParameter<int>(dataContextInfo.DataContext, field); | |
949 | |
950 ei.Queries[0].Parameters.Add(param); | |
951 | |
952 sqlQuery.Where.Field(field).Equal.Expr(param.SqlParameter); | |
953 | |
954 if (field.Nullable) | |
955 sqlQuery.IsParameterDependent = true; | |
956 } | |
957 | |
958 ei.SetNonQueryQuery(); | |
959 | |
960 ObjectOperation<T>.Delete.Add(key, ei); | |
961 } | |
962 | |
963 return (int)ei.GetElement(null, dataContextInfo, Expression.Constant(obj), null); | |
964 } | |
965 | |
966 #endregion | |
967 | |
968 #endregion | |
969 | |
970 #region New Builder Support | |
971 | |
972 public void SetElementQuery(Func<QueryContext,IDataContext,IDataReader,Expression,object[],object> mapper) | |
973 { | |
974 FinalizeQuery(); | |
975 | |
976 if (Queries.Count != 1) | |
977 throw new InvalidOperationException(); | |
978 | |
979 SqlProvider.SqlQuery = Queries[0].SqlQuery; | |
980 | |
981 GetElement = (ctx,db,expr,ps) => RunQuery(ctx, db,expr, ps, mapper); | |
982 } | |
983 | |
984 TE RunQuery<TE>( | |
985 QueryContext ctx, | |
986 IDataContextInfo dataContextInfo, | |
987 Expression expr, | |
988 object[] parameters, | |
989 Func<QueryContext,IDataContext,IDataReader,Expression,object[],TE> mapper) | |
990 { | |
991 var dataContext = dataContextInfo.DataContext; | |
992 | |
993 object query = null; | |
994 | |
995 try | |
996 { | |
997 query = SetCommand(dataContext, expr, parameters, 0); | |
998 | |
999 using (var dr = dataContext.ExecuteReader(query)) | |
1000 while (dr.Read()) | |
1001 return mapper(ctx, dataContext, dr, expr, parameters); | |
1002 | |
1003 return Array<TE>.Empty.First(); | |
1004 } | |
1005 finally | |
1006 { | |
1007 if (query != null) | |
1008 dataContext.ReleaseQuery(query); | |
1009 | |
1010 if (dataContextInfo.DisposeContext) | |
1011 dataContext.Dispose(); | |
1012 } | |
1013 } | |
1014 | |
1015 Func<IDataContextInfo,Expression,object[],int,IEnumerable<IDataReader>> GetQuery() | |
1016 { | |
1017 FinalizeQuery(); | |
1018 | |
1019 if (Queries.Count != 1) | |
1020 throw new InvalidOperationException(); | |
1021 | |
1022 Func<IDataContextInfo,Expression,object[],int,IEnumerable<IDataReader>> query = RunQuery; | |
1023 | |
1024 SqlProvider.SqlQuery = Queries[0].SqlQuery; | |
1025 | |
1026 var select = Queries[0].SqlQuery.Select; | |
1027 | |
1028 if (select.SkipValue != null && !SqlProvider.IsSkipSupported) | |
1029 { | |
1030 var q = query; | |
1031 | |
1032 if (select.SkipValue is SqlValue) | |
1033 { | |
1034 var n = (int)((IValueContainer)select.SkipValue).Value; | |
1035 | |
1036 if (n > 0) | |
1037 query = (db, expr, ps, qn) => q(db, expr, ps, qn).Skip(n); | |
1038 } | |
1039 else if (select.SkipValue is SqlParameter) | |
1040 { | |
1041 var i = GetParameterIndex(select.SkipValue); | |
1042 query = (db, expr, ps, qn) => q(db, expr, ps, qn).Skip((int)Queries[0].Parameters[i].Accessor(expr, ps)); | |
1043 } | |
1044 } | |
1045 | |
1046 if (select.TakeValue != null && !SqlProvider.IsTakeSupported) | |
1047 { | |
1048 var q = query; | |
1049 | |
1050 if (select.TakeValue is SqlValue) | |
1051 { | |
1052 var n = (int)((IValueContainer)select.TakeValue).Value; | |
1053 | |
1054 if (n > 0) | |
1055 query = (db, expr, ps, qn) => q(db, expr, ps, qn).Take(n); | |
1056 } | |
1057 else if (select.TakeValue is SqlParameter) | |
1058 { | |
1059 var i = GetParameterIndex(select.TakeValue); | |
1060 query = (db, expr, ps, qn) => q(db, expr, ps, qn).Take((int)Queries[0].Parameters[i].Accessor(expr, ps)); | |
1061 } | |
1062 } | |
1063 | |
1064 return query; | |
1065 } | |
1066 | |
1067 internal void SetQuery(Func<QueryContext,IDataContext,IDataReader,Expression,object[],T> mapper) | |
1068 { | |
1069 var query = GetQuery(); | |
1070 GetIEnumerable = (ctx,db,expr,ps) => Map(query(db, expr, ps, 0), ctx, db, expr, ps, mapper); | |
1071 } | |
1072 | |
1073 static IEnumerable<T> Map( | |
1074 IEnumerable<IDataReader> data, | |
1075 QueryContext queryContext, | |
1076 IDataContextInfo dataContextInfo, | |
1077 Expression expr, | |
1078 object[] ps, | |
1079 Func<QueryContext,IDataContext,IDataReader,Expression,object[],T> mapper) | |
1080 { | |
1081 if (queryContext == null) | |
1082 queryContext = new QueryContext(dataContextInfo, expr, ps); | |
1083 | |
1084 foreach (var dr in data) | |
1085 yield return mapper(queryContext, dataContextInfo.DataContext, dr, expr, ps); | |
1086 } | |
1087 | |
1088 internal void SetQuery(Func<QueryContext,IDataContext,IDataReader,Expression,object[],int,T> mapper) | |
1089 { | |
1090 var query = GetQuery(); | |
1091 GetIEnumerable = (ctx,db,expr,ps) => Map(query(db, expr, ps, 0), ctx, db, expr, ps, mapper); | |
1092 } | |
1093 | |
1094 static IEnumerable<T> Map( | |
1095 IEnumerable<IDataReader> data, | |
1096 QueryContext queryContext, | |
1097 IDataContextInfo dataContextInfo, | |
1098 Expression expr, | |
1099 object[] ps, | |
1100 Func<QueryContext,IDataContext,IDataReader,Expression,object[],int,T> mapper) | |
1101 { | |
1102 if (queryContext == null) | |
1103 queryContext = new QueryContext(dataContextInfo, expr, ps); | |
1104 | |
1105 var counter = 0; | |
1106 | |
1107 foreach (var dr in data) | |
1108 yield return mapper(queryContext, dataContextInfo.DataContext, dr, expr, ps, counter++); | |
1109 } | |
1110 | |
1111 #endregion | |
1112 } | |
1113 | |
1114 public class ParameterAccessor | |
1115 { | |
1116 public Expression Expression; | |
1117 public Func<Expression,object[],object> Accessor; | |
1118 public SqlParameter SqlParameter; | |
1119 } | |
1120 } |