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 }