[sre] Wrap mono_image_create_token with HANDLE_FUNCTION_{ENTER,RETURN}
[mono-project.git] / mcs / class / referencesource / System.Data.Linq / DataServices.cs
blob9a1cc8b165e8d49ac3c5de05584dc04419875838
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Collections.ObjectModel;
5 using System.Linq.Expressions;
6 using System.Reflection;
7 using System.Text;
8 using System.Linq;
9 using System.Runtime.CompilerServices;
11 namespace System.Data.Linq {
12 using System.Data.Linq.Mapping;
13 using System.Data.Linq.Provider;
15 internal class CommonDataServices : IDataServices {
16 DataContext context;
17 MetaModel metaModel;
18 IdentityManager identifier;
19 ChangeTracker tracker;
20 ChangeDirector director;
21 bool hasCachedObjects;
22 Dictionary<MetaDataMember, IDeferredSourceFactory> factoryMap;
24 internal CommonDataServices(DataContext context, MetaModel model) {
25 this.context = context;
26 this.metaModel = model;
27 bool asReadOnly = !context.ObjectTrackingEnabled;
28 this.identifier = IdentityManager.CreateIdentityManager(asReadOnly);
29 this.tracker = ChangeTracker.CreateChangeTracker(this, asReadOnly);
30 this.director = ChangeDirector.CreateChangeDirector(context);
31 this.factoryMap = new Dictionary<MetaDataMember, IDeferredSourceFactory>();
34 public DataContext Context {
35 get { return this.context; }
38 public MetaModel Model {
39 get { return this.metaModel; }
42 internal void SetModel(MetaModel model) {
43 this.metaModel = model;
46 internal IdentityManager IdentityManager {
47 get { return this.identifier; }
50 internal ChangeTracker ChangeTracker {
51 get { return this.tracker; }
54 internal ChangeDirector ChangeDirector {
55 get { return this.director; }
58 internal IEnumerable<RelatedItem> GetParents(MetaType type, object item) {
59 return this.GetRelations(type, item, true);
62 internal IEnumerable<RelatedItem> GetChildren(MetaType type, object item) {
63 return this.GetRelations(type, item, false);
66 private IEnumerable<RelatedItem> GetRelations(MetaType type, object item, bool isForeignKey) {
67 foreach (MetaDataMember mm in type.PersistentDataMembers) {
68 if (mm.IsAssociation) {
69 MetaType otherType = mm.Association.OtherType;
70 if (mm.Association.IsForeignKey == isForeignKey) {
71 object value = null;
72 if (mm.IsDeferred) {
73 value = mm.DeferredValueAccessor.GetBoxedValue(item);
75 else {
76 value = mm.StorageAccessor.GetBoxedValue(item);
78 if (value != null) {
79 if (mm.Association.IsMany) {
80 IEnumerable list = (IEnumerable)value;
81 foreach (object otherItem in list) {
82 yield return new RelatedItem(otherType.GetInheritanceType(otherItem.GetType()), otherItem);
85 else {
86 yield return new RelatedItem(otherType.GetInheritanceType(value.GetType()), value);
94 internal void ResetServices() {
95 hasCachedObjects = false;
96 bool asReadOnly = !context.ObjectTrackingEnabled;
97 this.identifier = IdentityManager.CreateIdentityManager(asReadOnly);
98 this.tracker = ChangeTracker.CreateChangeTracker(this, asReadOnly);
99 this.factoryMap = new Dictionary<MetaDataMember, IDeferredSourceFactory>();
102 internal static object[] GetKeyValues(MetaType type, object instance) {
103 List<object> keyValues = new List<object>();
104 foreach (MetaDataMember mm in type.IdentityMembers) {
105 keyValues.Add(mm.MemberAccessor.GetBoxedValue(instance));
107 return keyValues.ToArray();
110 internal static object[] GetForeignKeyValues(MetaAssociation association, object instance) {
111 List<object> keyValues = new List<object>();
112 foreach(MetaDataMember mm in association.ThisKey) {
113 keyValues.Add(mm.MemberAccessor.GetBoxedValue(instance));
115 return keyValues.ToArray();
118 internal object GetCachedObject(MetaType type, object[] keyValues) {
119 if( type == null ) {
120 throw Error.ArgumentNull("type");
122 if (!type.IsEntity) {
123 return null;
125 return this.identifier.Find(type, keyValues);
128 internal object GetCachedObjectLike(MetaType type, object instance) {
129 if( type == null ) {
130 throw Error.ArgumentNull("type");
132 if (!type.IsEntity) {
133 return null;
135 return this.identifier.FindLike(type, instance);
138 public bool IsCachedObject(MetaType type, object instance) {
139 if( type == null ) {
140 throw Error.ArgumentNull("type");
142 if (!type.IsEntity) {
143 return false;
145 return this.identifier.FindLike(type, instance) == instance;
148 public object InsertLookupCachedObject(MetaType type, object instance) {
149 if( type == null ) {
150 throw Error.ArgumentNull("type");
152 hasCachedObjects = true; // flag that we have cached objects
153 if (!type.IsEntity) {
154 return instance;
156 return this.identifier.InsertLookup(type, instance);
159 public bool RemoveCachedObjectLike(MetaType type, object instance) {
160 if (type == null) {
161 throw Error.ArgumentNull("type");
163 if (!type.IsEntity) {
164 return false;
166 return this.identifier.RemoveLike(type, instance);
169 public void OnEntityMaterialized(MetaType type, object instance) {
170 if (type == null) {
171 throw Error.ArgumentNull("type");
173 this.tracker.FastTrack(instance);
175 if (type.HasAnyLoadMethod) {
176 SendOnLoaded(type, instance);
180 private static void SendOnLoaded(MetaType type, object item) {
181 if (type != null) {
182 SendOnLoaded(type.InheritanceBase, item);
184 if (type.OnLoadedMethod != null) {
185 try {
186 type.OnLoadedMethod.Invoke(item, new object[] { });
187 } catch (TargetInvocationException tie) {
188 if (tie.InnerException != null) {
189 throw tie.InnerException;
192 throw;
199 /// <summary>
200 /// Returns a query for the entity indicated by the specified key.
201 /// </summary>
202 internal Expression GetObjectQuery(MetaType type, object[] keyValues) {
203 if (type == null) {
204 throw Error.ArgumentNull("type");
206 if (keyValues == null) {
207 throw Error.ArgumentNull("keyValues");
209 return this.GetObjectQuery(type, BuildKeyExpressions(keyValues, type.IdentityMembers));
212 internal Expression GetObjectQuery(MetaType type, Expression[] keyValues) {
213 ITable table = this.context.GetTable(type.InheritanceRoot.Type);
214 ParameterExpression serverItem = Expression.Parameter(table.ElementType, "p");
216 // create a where expression including all the identity members
217 Expression whereExpression = null;
218 for (int i = 0, n = type.IdentityMembers.Count; i < n; i++) {
219 MetaDataMember metaMember = type.IdentityMembers[i];
220 Expression memberExpression = (metaMember.Member is FieldInfo)
221 ? Expression.Field(serverItem, (FieldInfo)metaMember.Member)
222 : Expression.Property(serverItem, (PropertyInfo)metaMember.Member);
223 Expression memberEqualityExpression = Expression.Equal(memberExpression, keyValues[i]);
224 whereExpression = (whereExpression != null)
225 ? Expression.And(whereExpression, memberEqualityExpression)
226 : memberEqualityExpression;
228 return Expression.Call(typeof(Queryable), "Where", new Type[] { table.ElementType }, table.Expression, Expression.Lambda(whereExpression, serverItem));
231 internal Expression GetDataMemberQuery(MetaDataMember member, Expression[] keyValues) {
232 if (member == null)
233 throw Error.ArgumentNull("member");
234 if (keyValues == null)
235 throw Error.ArgumentNull("keyValues");
236 if (member.IsAssociation) {
237 MetaAssociation association = member.Association;
238 Type rootType = association.ThisMember.DeclaringType.InheritanceRoot.Type;
239 Expression thisSource = Expression.Constant(context.GetTable(rootType));
240 if (rootType != association.ThisMember.DeclaringType.Type) {
241 thisSource = Expression.Call(typeof(Enumerable), "Cast", new Type[] { association.ThisMember.DeclaringType.Type }, thisSource);
243 Expression thisInstance = Expression.Call(typeof(Enumerable), "FirstOrDefault", new Type[] { association.ThisMember.DeclaringType.Type },
244 System.Data.Linq.SqlClient.Translator.WhereClauseFromSourceAndKeys(thisSource, association.ThisKey.ToArray(), keyValues)
246 Expression otherSource = Expression.Constant(context.GetTable(association.OtherType.InheritanceRoot.Type));
247 if (association.OtherType.Type!=association.OtherType.InheritanceRoot.Type) {
248 otherSource = Expression.Call(typeof(Enumerable), "Cast", new Type[] { association.OtherType.Type }, otherSource);
250 Expression expr = System.Data.Linq.SqlClient.Translator.TranslateAssociation(
251 this.context, association, otherSource, keyValues, thisInstance
253 return expr;
255 else {
256 Expression query = this.GetObjectQuery(member.DeclaringType, keyValues);
257 Type elementType = System.Data.Linq.SqlClient.TypeSystem.GetElementType(query.Type);
258 ParameterExpression p = Expression.Parameter(elementType, "p");
259 Expression e = p;
260 if (elementType != member.DeclaringType.Type)
261 e = Expression.Convert(e, member.DeclaringType.Type);
262 Expression mem = (member.Member is PropertyInfo)
263 ? Expression.Property(e, (PropertyInfo)member.Member)
264 : Expression.Field(e, (FieldInfo)member.Member);
265 LambdaExpression selector = Expression.Lambda(mem, p);
266 return Expression.Call(typeof(Queryable), "Select", new Type[] { elementType, selector.Body.Type }, query, selector);
270 private static Expression[] BuildKeyExpressions(object[] keyValues, ReadOnlyCollection<MetaDataMember> keyMembers) {
271 Expression[] keyValueExpressions = new Expression[keyValues.Length];
272 for (int i = 0, n = keyMembers.Count; i < n; i++) {
273 MetaDataMember metaMember = keyMembers[i];
274 Expression keyValueExpression = Expression.Constant(keyValues[i], metaMember.Type);
275 keyValueExpressions[i] = keyValueExpression;
277 return keyValueExpressions;
280 [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
281 public IDeferredSourceFactory GetDeferredSourceFactory(MetaDataMember member) {
282 if (member == null) {
283 throw Error.ArgumentNull("member");
285 IDeferredSourceFactory factory;
286 if (this.factoryMap.TryGetValue(member, out factory)) {
287 return factory;
289 Type elemType = member.IsAssociation && member.Association.IsMany
290 ? System.Data.Linq.SqlClient.TypeSystem.GetElementType(member.Type)
291 : member.Type;
292 factory = (IDeferredSourceFactory) Activator.CreateInstance(
293 typeof(DeferredSourceFactory<>).MakeGenericType(elemType),
294 BindingFlags.Instance | BindingFlags.NonPublic, null,
295 new object[] { member, this }, null
297 this.factoryMap.Add(member, factory);
298 return factory;
301 class DeferredSourceFactory<T> : IDeferredSourceFactory {
302 MetaDataMember member;
303 CommonDataServices services;
304 ICompiledQuery query;
305 bool refersToPrimaryKey;
306 T[] empty;
308 internal DeferredSourceFactory(MetaDataMember member, CommonDataServices services) {
309 this.member = member;
310 this.services = services;
311 this.refersToPrimaryKey = this.member.IsAssociation && this.member.Association.OtherKeyIsPrimaryKey;
312 this.empty = new T[] { };
315 public IEnumerable CreateDeferredSource(object instance) {
316 if (instance == null)
317 throw Error.ArgumentNull("instance");
318 return new DeferredSource(this, instance);
321 public IEnumerable CreateDeferredSource(object[] keyValues) {
322 if (keyValues == null)
323 throw Error.ArgumentNull("keyValues");
324 return new DeferredSource(this, keyValues);
327 private IEnumerator<T> Execute(object instance) {
328 ReadOnlyCollection<MetaDataMember> keys = null;
329 if (this.member.IsAssociation) {
330 keys = this.member.Association.ThisKey;
332 else {
333 keys = this.member.DeclaringType.IdentityMembers;
335 object[] keyValues = new object[keys.Count];
336 for (int i = 0, n = keys.Count; i < n; i++) {
337 object value = keys[i].StorageAccessor.GetBoxedValue(instance);
338 keyValues[i] = value;
341 if (this.HasNullForeignKey(keyValues)) {
342 return ((IEnumerable<T>)this.empty).GetEnumerator();
345 T cached;
346 if (this.TryGetCachedObject(keyValues, out cached)) {
347 return ((IEnumerable<T>)(new T[] { cached })).GetEnumerator();
350 if (this.member.LoadMethod != null) {
351 try {
352 object result = this.member.LoadMethod.Invoke(this.services.Context, new object[] { instance });
353 if (typeof(T).IsAssignableFrom(this.member.LoadMethod.ReturnType)) {
354 return ((IEnumerable<T>)new T[] { (T)result }).GetEnumerator();
356 else {
357 return ((IEnumerable<T>)result).GetEnumerator();
360 catch (TargetInvocationException tie) {
361 if (tie.InnerException != null) {
362 throw tie.InnerException;
364 throw;
367 else {
368 return this.ExecuteKeyQuery(keyValues);
372 private IEnumerator<T> ExecuteKeys(object[] keyValues) {
373 if (this.HasNullForeignKey(keyValues)) {
374 return ((IEnumerable<T>)this.empty).GetEnumerator();
377 T cached;
378 if (this.TryGetCachedObject(keyValues, out cached)) {
379 return ((IEnumerable<T>)(new T[] { cached })).GetEnumerator();
382 return this.ExecuteKeyQuery(keyValues);
385 private bool HasNullForeignKey(object[] keyValues) {
386 if (this.refersToPrimaryKey) {
387 bool keyHasNull = false;
388 for (int i = 0, n = keyValues.Length; i < n; i++) {
389 keyHasNull |= keyValues[i] == null;
391 if (keyHasNull) {
392 return true;
395 return false;
398 private bool TryGetCachedObject(object[] keyValues, out T cached) {
399 cached = default(T);
400 if (this.refersToPrimaryKey) {
401 // look to see if we already have this object in the identity cache
402 MetaType mt = this.member.IsAssociation ? this.member.Association.OtherType : this.member.DeclaringType;
403 object obj = this.services.GetCachedObject(mt, keyValues);
404 if (obj != null) {
405 cached = (T)obj;
406 return true;
409 return false;
412 private IEnumerator<T> ExecuteKeyQuery(object[] keyValues) {
413 if (this.query == null) {
414 ParameterExpression p = Expression.Parameter(typeof(object[]), "keys");
415 Expression[] keyExprs = new Expression[keyValues.Length];
416 ReadOnlyCollection<MetaDataMember> members = this.member.IsAssociation ? this.member.Association.OtherKey : this.member.DeclaringType.IdentityMembers;
417 for (int i = 0, n = keyValues.Length; i < n; i++) {
418 MetaDataMember mm = members[i];
419 keyExprs[i] = Expression.Convert(
420 #pragma warning disable 618 // Disable the 'obsolete' warning
421 Expression.ArrayIndex(p, Expression.Constant(i)),
422 #pragma warning restore 618
423 mm.Type
426 Expression q = this.services.GetDataMemberQuery(this.member, keyExprs);
427 LambdaExpression lambda = Expression.Lambda(q, p);
428 this.query = this.services.Context.Provider.Compile(lambda);
430 return ((IEnumerable<T>)this.query.Execute(this.services.Context.Provider, new object[] { keyValues }).ReturnValue).GetEnumerator();
433 class DeferredSource : IEnumerable<T>, IEnumerable {
434 DeferredSourceFactory<T> factory;
435 object instance;
437 internal DeferredSource(DeferredSourceFactory<T> factory, object instance) {
438 this.factory = factory;
439 this.instance = instance;
442 public IEnumerator<T> GetEnumerator() {
443 object[] keyValues = this.instance as object[];
444 if (keyValues != null) {
445 return this.factory.ExecuteKeys(keyValues);
447 return this.factory.Execute(this.instance);
450 IEnumerator IEnumerable.GetEnumerator() {
451 return this.GetEnumerator();
456 /// <summary>
457 /// Returns true if any objects have been added to the identity cache. If
458 /// object tracking is disabled, this still returns true if any attempts
459 /// where made to cache an object. Thus regardless of object tracking mode,
460 /// this can be used as an indicator as to whether any result returning queries
461 /// have been executed.
462 /// </summary>
463 internal bool HasCachedObjects {
464 get {
465 return this.hasCachedObjects;
469 public object GetCachedObject(Expression query) {
470 if (query == null)
471 return null;
472 MethodCallExpression mc = query as MethodCallExpression;
473 if (mc == null || mc.Arguments.Count < 1 || mc.Arguments.Count > 2)
474 return null;
475 if (mc.Method.DeclaringType != typeof(Queryable)) {
476 return null;
478 switch (mc.Method.Name) {
479 case "Where":
480 case "First":
481 case "FirstOrDefault":
482 case "Single":
483 case "SingleOrDefault":
484 break;
485 default:
486 return null;
488 if (mc.Arguments.Count == 1) {
489 // If it is something like
490 // context.Customers.Where(c => c.ID = 123).First()
491 // then it is equivalent of
492 // context.Customers.First(c => c.ID = 123)
493 // hence reduce to context.Customers.Where(c => c.ID = 123) and process the remaining query
494 return GetCachedObject(mc.Arguments[0]);
496 UnaryExpression quote = mc.Arguments[1] as UnaryExpression;
497 if (quote == null || quote.NodeType != ExpressionType.Quote)
498 return null;
499 LambdaExpression pred = quote.Operand as LambdaExpression;
500 if (pred == null)
501 return null;
502 ConstantExpression cex = mc.Arguments[0] as ConstantExpression;
503 if (cex == null)
504 return null;
505 ITable t = cex.Value as ITable;
506 if (t == null)
507 return null;
508 Type elementType = System.Data.Linq.SqlClient.TypeSystem.GetElementType(query.Type);
509 if (elementType != t.ElementType)
510 return null;
511 MetaTable metaTable = this.metaModel.GetTable(t.ElementType);
512 object[] keyValues = this.GetKeyValues(metaTable.RowType, pred);
513 if (keyValues != null) {
514 return this.GetCachedObject(metaTable.RowType, keyValues);
516 return null;
519 internal object[] GetKeyValues(MetaType type, LambdaExpression predicate) {
520 if (predicate == null)
521 throw Error.ArgumentNull("predicate");
522 if (predicate.Parameters.Count != 1)
523 return null;
524 Dictionary<MetaDataMember, object> keys = new Dictionary<MetaDataMember, object>();
525 if (this.GetKeysFromPredicate(type, keys, predicate.Body)
526 && keys.Count == type.IdentityMembers.Count) {
527 object[] values = keys.OrderBy(kv => kv.Key.Ordinal).Select(kv => kv.Value).ToArray();
528 return values;
530 return null;
533 private bool GetKeysFromPredicate(MetaType type, Dictionary<MetaDataMember, object> keys, Expression expr) {
534 BinaryExpression bex = expr as BinaryExpression;
535 if (bex == null) {
536 MethodCallExpression mex = expr as MethodCallExpression;
537 if (mex != null && mex.Method.Name == "op_Equality" && mex.Arguments.Count == 2) {
538 bex = Expression.Equal(mex.Arguments[0], mex.Arguments[1]);
540 else {
541 return false;
544 switch (bex.NodeType) {
545 case ExpressionType.And:
546 return this.GetKeysFromPredicate(type, keys, bex.Left) &&
547 this.GetKeysFromPredicate(type, keys, bex.Right);
548 case ExpressionType.Equal:
549 return GetKeyFromPredicate(type, keys, bex.Left, bex.Right) ||
550 GetKeyFromPredicate(type, keys, bex.Right, bex.Left);
551 default:
552 return false;
556 private static bool GetKeyFromPredicate(MetaType type, Dictionary<MetaDataMember, object> keys, Expression mex, Expression vex) {
557 MemberExpression memex = mex as MemberExpression;
558 if (memex == null || memex.Expression == null ||
559 memex.Expression.NodeType != ExpressionType.Parameter || memex.Expression.Type != type.Type) {
560 return false;
562 if (!type.Type.IsAssignableFrom(memex.Member.ReflectedType) && !memex.Member.ReflectedType.IsAssignableFrom(type.Type)) {
563 return false;
565 MetaDataMember mm = type.GetDataMember(memex.Member);
566 if (!mm.IsPrimaryKey) {
567 return false;
569 if (keys.ContainsKey(mm)) {
570 return false;
572 ConstantExpression cex = vex as ConstantExpression;
573 if (cex != null) {
574 keys.Add(mm, cex.Value);
575 return true;
577 InvocationExpression ie = vex as InvocationExpression;
578 if (ie != null && ie.Arguments != null && ie.Arguments.Count == 0) {
579 ConstantExpression ce = ie.Expression as ConstantExpression;
580 if (ce != null) {
581 keys.Add(mm, ((Delegate)ce.Value).DynamicInvoke(new object[] {}));
582 return true;
585 return false;
588 /// <summary>
589 /// Either returns the object from cache if it is in cache, or
590 /// queries for it.
591 /// </summary>
592 internal object GetObjectByKey(MetaType type, object[] keyValues) {
593 // first check the cache
594 object target = GetCachedObject(type, keyValues);
595 if (target == null) {
596 // no cached value, so query for it
597 target = ((IEnumerable)this.context.Provider.Execute(this.GetObjectQuery(type, keyValues)).ReturnValue).OfType<object>().SingleOrDefault();
599 return target;
603 internal struct RelatedItem {
604 internal MetaType Type;
605 internal object Item;
606 internal RelatedItem(MetaType type, object item) {
607 this.Type = type;
608 this.Item = item;