2 using System
.Collections
;
3 using System
.Collections
.Generic
;
4 using System
.Collections
.ObjectModel
;
5 using System
.Linq
.Expressions
;
6 using System
.Reflection
;
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
{
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
) {
73 value = mm
.DeferredValueAccessor
.GetBoxedValue(item
);
76 value = mm
.StorageAccessor
.GetBoxedValue(item
);
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
);
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
) {
120 throw Error
.ArgumentNull("type");
122 if (!type
.IsEntity
) {
125 return this.identifier
.Find(type
, keyValues
);
128 internal object GetCachedObjectLike(MetaType type
, object instance
) {
130 throw Error
.ArgumentNull("type");
132 if (!type
.IsEntity
) {
135 return this.identifier
.FindLike(type
, instance
);
138 public bool IsCachedObject(MetaType type
, object instance
) {
140 throw Error
.ArgumentNull("type");
142 if (!type
.IsEntity
) {
145 return this.identifier
.FindLike(type
, instance
) == instance
;
148 public object InsertLookupCachedObject(MetaType type
, object instance
) {
150 throw Error
.ArgumentNull("type");
152 hasCachedObjects
= true; // flag that we have cached objects
153 if (!type
.IsEntity
) {
156 return this.identifier
.InsertLookup(type
, instance
);
159 public bool RemoveCachedObjectLike(MetaType type
, object instance
) {
161 throw Error
.ArgumentNull("type");
163 if (!type
.IsEntity
) {
166 return this.identifier
.RemoveLike(type
, instance
);
169 public void OnEntityMaterialized(MetaType type
, object instance
) {
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
) {
182 SendOnLoaded(type
.InheritanceBase
, item
);
184 if (type
.OnLoadedMethod
!= null) {
186 type
.OnLoadedMethod
.Invoke(item
, new object[] { }
);
187 } catch (TargetInvocationException tie
) {
188 if (tie
.InnerException
!= null) {
189 throw tie
.InnerException
;
200 /// Returns a query for the entity indicated by the specified key.
202 internal Expression
GetObjectQuery(MetaType type
, object[] keyValues
) {
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
) {
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
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");
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
)) {
289 Type elemType
= member
.IsAssociation
&& member
.Association
.IsMany
290 ? System
.Data
.Linq
.SqlClient
.TypeSystem
.GetElementType(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
);
301 class DeferredSourceFactory
<T
> : IDeferredSourceFactory
{
302 MetaDataMember member
;
303 CommonDataServices services
;
304 ICompiledQuery query
;
305 bool refersToPrimaryKey
;
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
;
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();
346 if (this.TryGetCachedObject(keyValues
, out cached
)) {
347 return ((IEnumerable
<T
>)(new T
[] { cached }
)).GetEnumerator();
350 if (this.member
.LoadMethod
!= null) {
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();
357 return ((IEnumerable
<T
>)result
).GetEnumerator();
360 catch (TargetInvocationException tie
) {
361 if (tie
.InnerException
!= null) {
362 throw tie
.InnerException
;
368 return this.ExecuteKeyQuery(keyValues
);
372 private IEnumerator
<T
> ExecuteKeys(object[] keyValues
) {
373 if (this.HasNullForeignKey(keyValues
)) {
374 return ((IEnumerable
<T
>)this.empty
).GetEnumerator();
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;
398 private bool TryGetCachedObject(object[] keyValues
, out T cached
) {
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
);
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
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
;
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();
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.
463 internal bool HasCachedObjects
{
465 return this.hasCachedObjects
;
469 public object GetCachedObject(Expression query
) {
472 MethodCallExpression mc
= query
as MethodCallExpression
;
473 if (mc
== null || mc
.Arguments
.Count
< 1 || mc
.Arguments
.Count
> 2)
475 if (mc
.Method
.DeclaringType
!= typeof(Queryable
)) {
478 switch (mc
.Method
.Name
) {
481 case "FirstOrDefault":
483 case "SingleOrDefault":
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
)
499 LambdaExpression pred
= quote
.Operand
as LambdaExpression
;
502 ConstantExpression cex
= mc
.Arguments
[0] as ConstantExpression
;
505 ITable t
= cex
.Value
as ITable
;
508 Type elementType
= System
.Data
.Linq
.SqlClient
.TypeSystem
.GetElementType(query
.Type
);
509 if (elementType
!= t
.ElementType
)
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
);
519 internal object[] GetKeyValues(MetaType type
, LambdaExpression predicate
) {
520 if (predicate
== null)
521 throw Error
.ArgumentNull("predicate");
522 if (predicate
.Parameters
.Count
!= 1)
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();
533 private bool GetKeysFromPredicate(MetaType type
, Dictionary
<MetaDataMember
, object> keys
, Expression expr
) {
534 BinaryExpression bex
= expr
as BinaryExpression
;
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]);
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
);
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
) {
562 if (!type
.Type
.IsAssignableFrom(memex
.Member
.ReflectedType
) && !memex
.Member
.ReflectedType
.IsAssignableFrom(type
.Type
)) {
565 MetaDataMember mm
= type
.GetDataMember(memex
.Member
);
566 if (!mm
.IsPrimaryKey
) {
569 if (keys
.ContainsKey(mm
)) {
572 ConstantExpression cex
= vex
as ConstantExpression
;
574 keys
.Add(mm
, cex
.Value
);
577 InvocationExpression ie
= vex
as InvocationExpression
;
578 if (ie
!= null && ie
.Arguments
!= null && ie
.Arguments
.Count
== 0) {
579 ConstantExpression ce
= ie
.Expression
as ConstantExpression
;
581 keys
.Add(mm
, ((Delegate
)ce
.Value
).DynamicInvoke(new object[] {}));
589 /// Either returns the object from cache if it is in cache, or
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();
603 internal struct RelatedItem
{
604 internal MetaType Type
;
605 internal object Item
;
606 internal RelatedItem(MetaType type
, object item
) {