0
|
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 }
|