| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Text;
- using System.Linq;
- using System.Runtime.CompilerServices;
- namespace System.Data.Linq {
- using System.Data.Linq.Mapping;
- using System.Data.Linq.Provider;
- internal class CommonDataServices : IDataServices {
- DataContext context;
- MetaModel metaModel;
- IdentityManager identifier;
- ChangeTracker tracker;
- ChangeDirector director;
- bool hasCachedObjects;
- Dictionary<MetaDataMember, IDeferredSourceFactory> factoryMap;
- internal CommonDataServices(DataContext context, MetaModel model) {
- this.context = context;
- this.metaModel = model;
- bool asReadOnly = !context.ObjectTrackingEnabled;
- this.identifier = IdentityManager.CreateIdentityManager(asReadOnly);
- this.tracker = ChangeTracker.CreateChangeTracker(this, asReadOnly);
- this.director = ChangeDirector.CreateChangeDirector(context);
- this.factoryMap = new Dictionary<MetaDataMember, IDeferredSourceFactory>();
- }
- public DataContext Context {
- get { return this.context; }
- }
- public MetaModel Model {
- get { return this.metaModel; }
- }
- internal void SetModel(MetaModel model) {
- this.metaModel = model;
- }
- internal IdentityManager IdentityManager {
- get { return this.identifier; }
- }
- internal ChangeTracker ChangeTracker {
- get { return this.tracker; }
- }
- internal ChangeDirector ChangeDirector {
- get { return this.director; }
- }
- internal IEnumerable<RelatedItem> GetParents(MetaType type, object item) {
- return this.GetRelations(type, item, true);
- }
- internal IEnumerable<RelatedItem> GetChildren(MetaType type, object item) {
- return this.GetRelations(type, item, false);
- }
- private IEnumerable<RelatedItem> GetRelations(MetaType type, object item, bool isForeignKey) {
- foreach (MetaDataMember mm in type.PersistentDataMembers) {
- if (mm.IsAssociation) {
- MetaType otherType = mm.Association.OtherType;
- if (mm.Association.IsForeignKey == isForeignKey) {
- object value = null;
- if (mm.IsDeferred) {
- value = mm.DeferredValueAccessor.GetBoxedValue(item);
- }
- else {
- value = mm.StorageAccessor.GetBoxedValue(item);
- }
- if (value != null) {
- if (mm.Association.IsMany) {
- IEnumerable list = (IEnumerable)value;
- foreach (object otherItem in list) {
- yield return new RelatedItem(otherType.GetInheritanceType(otherItem.GetType()), otherItem);
- }
- }
- else {
- yield return new RelatedItem(otherType.GetInheritanceType(value.GetType()), value);
- }
- }
- }
- }
- }
- }
- internal void ResetServices() {
- hasCachedObjects = false;
- bool asReadOnly = !context.ObjectTrackingEnabled;
- this.identifier = IdentityManager.CreateIdentityManager(asReadOnly);
- this.tracker = ChangeTracker.CreateChangeTracker(this, asReadOnly);
- this.factoryMap = new Dictionary<MetaDataMember, IDeferredSourceFactory>();
- }
- internal static object[] GetKeyValues(MetaType type, object instance) {
- List<object> keyValues = new List<object>();
- foreach (MetaDataMember mm in type.IdentityMembers) {
- keyValues.Add(mm.MemberAccessor.GetBoxedValue(instance));
- }
- return keyValues.ToArray();
- }
- internal static object[] GetForeignKeyValues(MetaAssociation association, object instance) {
- List<object> keyValues = new List<object>();
- foreach(MetaDataMember mm in association.ThisKey) {
- keyValues.Add(mm.MemberAccessor.GetBoxedValue(instance));
- }
- return keyValues.ToArray();
- }
- internal object GetCachedObject(MetaType type, object[] keyValues) {
- if( type == null ) {
- throw Error.ArgumentNull("type");
- }
- if (!type.IsEntity) {
- return null;
- }
- return this.identifier.Find(type, keyValues);
- }
- internal object GetCachedObjectLike(MetaType type, object instance) {
- if( type == null ) {
- throw Error.ArgumentNull("type");
- }
- if (!type.IsEntity) {
- return null;
- }
- return this.identifier.FindLike(type, instance);
- }
- public bool IsCachedObject(MetaType type, object instance) {
- if( type == null ) {
- throw Error.ArgumentNull("type");
- }
- if (!type.IsEntity) {
- return false;
- }
- return this.identifier.FindLike(type, instance) == instance;
- }
- public object InsertLookupCachedObject(MetaType type, object instance) {
- if( type == null ) {
- throw Error.ArgumentNull("type");
- }
- hasCachedObjects = true; // flag that we have cached objects
- if (!type.IsEntity) {
- return instance;
- }
- return this.identifier.InsertLookup(type, instance);
- }
- public bool RemoveCachedObjectLike(MetaType type, object instance) {
- if (type == null) {
- throw Error.ArgumentNull("type");
- }
- if (!type.IsEntity) {
- return false;
- }
- return this.identifier.RemoveLike(type, instance);
- }
- public void OnEntityMaterialized(MetaType type, object instance) {
- if (type == null) {
- throw Error.ArgumentNull("type");
- }
- this.tracker.FastTrack(instance);
- if (type.HasAnyLoadMethod) {
- SendOnLoaded(type, instance);
- }
- }
- private static void SendOnLoaded(MetaType type, object item) {
- if (type != null) {
- SendOnLoaded(type.InheritanceBase, item);
- if (type.OnLoadedMethod != null) {
- try {
- type.OnLoadedMethod.Invoke(item, new object[] { });
- } catch (TargetInvocationException tie) {
- if (tie.InnerException != null) {
- throw tie.InnerException;
- }
- throw;
- }
- }
- }
- }
- /// <summary>
- /// Returns a query for the entity indicated by the specified key.
- /// </summary>
- internal Expression GetObjectQuery(MetaType type, object[] keyValues) {
- if (type == null) {
- throw Error.ArgumentNull("type");
- }
- if (keyValues == null) {
- throw Error.ArgumentNull("keyValues");
- }
- return this.GetObjectQuery(type, BuildKeyExpressions(keyValues, type.IdentityMembers));
- }
- internal Expression GetObjectQuery(MetaType type, Expression[] keyValues) {
- ITable table = this.context.GetTable(type.InheritanceRoot.Type);
- ParameterExpression serverItem = Expression.Parameter(table.ElementType, "p");
- // create a where expression including all the identity members
- Expression whereExpression = null;
- for (int i = 0, n = type.IdentityMembers.Count; i < n; i++) {
- MetaDataMember metaMember = type.IdentityMembers[i];
- Expression memberExpression = (metaMember.Member is FieldInfo)
- ? Expression.Field(serverItem, (FieldInfo)metaMember.Member)
- : Expression.Property(serverItem, (PropertyInfo)metaMember.Member);
- Expression memberEqualityExpression = Expression.Equal(memberExpression, keyValues[i]);
- whereExpression = (whereExpression != null)
- ? Expression.And(whereExpression, memberEqualityExpression)
- : memberEqualityExpression;
- }
- return Expression.Call(typeof(Queryable), "Where", new Type[] { table.ElementType }, table.Expression, Expression.Lambda(whereExpression, serverItem));
- }
- internal Expression GetDataMemberQuery(MetaDataMember member, Expression[] keyValues) {
- if (member == null)
- throw Error.ArgumentNull("member");
- if (keyValues == null)
- throw Error.ArgumentNull("keyValues");
- if (member.IsAssociation) {
- MetaAssociation association = member.Association;
- Type rootType = association.ThisMember.DeclaringType.InheritanceRoot.Type;
- Expression thisSource = Expression.Constant(context.GetTable(rootType));
- if (rootType != association.ThisMember.DeclaringType.Type) {
- thisSource = Expression.Call(typeof(Enumerable), "Cast", new Type[] { association.ThisMember.DeclaringType.Type }, thisSource);
- }
- Expression thisInstance = Expression.Call(typeof(Enumerable), "FirstOrDefault", new Type[] { association.ThisMember.DeclaringType.Type },
- System.Data.Linq.SqlClient.Translator.WhereClauseFromSourceAndKeys(thisSource, association.ThisKey.ToArray(), keyValues)
- );
- Expression otherSource = Expression.Constant(context.GetTable(association.OtherType.InheritanceRoot.Type));
- if (association.OtherType.Type!=association.OtherType.InheritanceRoot.Type) {
- otherSource = Expression.Call(typeof(Enumerable), "Cast", new Type[] { association.OtherType.Type }, otherSource);
- }
- Expression expr = System.Data.Linq.SqlClient.Translator.TranslateAssociation(
- this.context, association, otherSource, keyValues, thisInstance
- );
- return expr;
- }
- else {
- Expression query = this.GetObjectQuery(member.DeclaringType, keyValues);
- Type elementType = System.Data.Linq.SqlClient.TypeSystem.GetElementType(query.Type);
- ParameterExpression p = Expression.Parameter(elementType, "p");
- Expression e = p;
- if (elementType != member.DeclaringType.Type)
- e = Expression.Convert(e, member.DeclaringType.Type);
- Expression mem = (member.Member is PropertyInfo)
- ? Expression.Property(e, (PropertyInfo)member.Member)
- : Expression.Field(e, (FieldInfo)member.Member);
- LambdaExpression selector = Expression.Lambda(mem, p);
- return Expression.Call(typeof(Queryable), "Select", new Type[] { elementType, selector.Body.Type }, query, selector);
- }
- }
- private static Expression[] BuildKeyExpressions(object[] keyValues, ReadOnlyCollection<MetaDataMember> keyMembers) {
- Expression[] keyValueExpressions = new Expression[keyValues.Length];
- for (int i = 0, n = keyMembers.Count; i < n; i++) {
- MetaDataMember metaMember = keyMembers[i];
- Expression keyValueExpression = Expression.Constant(keyValues[i], metaMember.Type);
- keyValueExpressions[i] = keyValueExpression;
- }
- return keyValueExpressions;
- }
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- public IDeferredSourceFactory GetDeferredSourceFactory(MetaDataMember member) {
- if (member == null) {
- throw Error.ArgumentNull("member");
- }
- IDeferredSourceFactory factory;
- if (this.factoryMap.TryGetValue(member, out factory)) {
- return factory;
- }
- Type elemType = member.IsAssociation && member.Association.IsMany
- ? System.Data.Linq.SqlClient.TypeSystem.GetElementType(member.Type)
- : member.Type;
- factory = (IDeferredSourceFactory) Activator.CreateInstance(
- typeof(DeferredSourceFactory<>).MakeGenericType(elemType),
- BindingFlags.Instance | BindingFlags.NonPublic, null,
- new object[] { member, this }, null
- );
- this.factoryMap.Add(member, factory);
- return factory;
- }
- class DeferredSourceFactory<T> : IDeferredSourceFactory {
- MetaDataMember member;
- CommonDataServices services;
- ICompiledQuery query;
- bool refersToPrimaryKey;
- T[] empty;
- internal DeferredSourceFactory(MetaDataMember member, CommonDataServices services) {
- this.member = member;
- this.services = services;
- this.refersToPrimaryKey = this.member.IsAssociation && this.member.Association.OtherKeyIsPrimaryKey;
- this.empty = new T[] { };
- }
- public IEnumerable CreateDeferredSource(object instance) {
- if (instance == null)
- throw Error.ArgumentNull("instance");
- return new DeferredSource(this, instance);
- }
- public IEnumerable CreateDeferredSource(object[] keyValues) {
- if (keyValues == null)
- throw Error.ArgumentNull("keyValues");
- return new DeferredSource(this, keyValues);
- }
- private IEnumerator<T> Execute(object instance) {
- ReadOnlyCollection<MetaDataMember> keys = null;
- if (this.member.IsAssociation) {
- keys = this.member.Association.ThisKey;
- }
- else {
- keys = this.member.DeclaringType.IdentityMembers;
- }
- object[] keyValues = new object[keys.Count];
- for (int i = 0, n = keys.Count; i < n; i++) {
- object value = keys[i].StorageAccessor.GetBoxedValue(instance);
- keyValues[i] = value;
- }
- if (this.HasNullForeignKey(keyValues)) {
- return ((IEnumerable<T>)this.empty).GetEnumerator();
- }
- T cached;
- if (this.TryGetCachedObject(keyValues, out cached)) {
- return ((IEnumerable<T>)(new T[] { cached })).GetEnumerator();
- }
- if (this.member.LoadMethod != null) {
- try {
- object result = this.member.LoadMethod.Invoke(this.services.Context, new object[] { instance });
- if (typeof(T).IsAssignableFrom(this.member.LoadMethod.ReturnType)) {
- return ((IEnumerable<T>)new T[] { (T)result }).GetEnumerator();
- }
- else {
- return ((IEnumerable<T>)result).GetEnumerator();
- }
- }
- catch (TargetInvocationException tie) {
- if (tie.InnerException != null) {
- throw tie.InnerException;
- }
- throw;
- }
- }
- else {
- return this.ExecuteKeyQuery(keyValues);
- }
- }
- private IEnumerator<T> ExecuteKeys(object[] keyValues) {
- if (this.HasNullForeignKey(keyValues)) {
- return ((IEnumerable<T>)this.empty).GetEnumerator();
- }
- T cached;
- if (this.TryGetCachedObject(keyValues, out cached)) {
- return ((IEnumerable<T>)(new T[] { cached })).GetEnumerator();
- }
- return this.ExecuteKeyQuery(keyValues);
- }
- private bool HasNullForeignKey(object[] keyValues) {
- if (this.refersToPrimaryKey) {
- bool keyHasNull = false;
- for (int i = 0, n = keyValues.Length; i < n; i++) {
- keyHasNull |= keyValues[i] == null;
- }
- if (keyHasNull) {
- return true;
- }
- }
- return false;
- }
- private bool TryGetCachedObject(object[] keyValues, out T cached) {
- cached = default(T);
- if (this.refersToPrimaryKey) {
- // look to see if we already have this object in the identity cache
- MetaType mt = this.member.IsAssociation ? this.member.Association.OtherType : this.member.DeclaringType;
- object obj = this.services.GetCachedObject(mt, keyValues);
- if (obj != null) {
- cached = (T)obj;
- return true;
- }
- }
- return false;
- }
- private IEnumerator<T> ExecuteKeyQuery(object[] keyValues) {
- if (this.query == null) {
- ParameterExpression p = Expression.Parameter(typeof(object[]), "keys");
- Expression[] keyExprs = new Expression[keyValues.Length];
- ReadOnlyCollection<MetaDataMember> members = this.member.IsAssociation ? this.member.Association.OtherKey : this.member.DeclaringType.IdentityMembers;
- for (int i = 0, n = keyValues.Length; i < n; i++) {
- MetaDataMember mm = members[i];
- keyExprs[i] = Expression.Convert(
- #pragma warning disable 618 // Disable the 'obsolete' warning
- Expression.ArrayIndex(p, Expression.Constant(i)),
- #pragma warning restore 618
- mm.Type
- );
- }
- Expression q = this.services.GetDataMemberQuery(this.member, keyExprs);
- LambdaExpression lambda = Expression.Lambda(q, p);
- this.query = this.services.Context.Provider.Compile(lambda);
- }
- return ((IEnumerable<T>)this.query.Execute(this.services.Context.Provider, new object[] { keyValues }).ReturnValue).GetEnumerator();
- }
- class DeferredSource : IEnumerable<T>, IEnumerable {
- DeferredSourceFactory<T> factory;
- object instance;
- internal DeferredSource(DeferredSourceFactory<T> factory, object instance) {
- this.factory = factory;
- this.instance = instance;
- }
- public IEnumerator<T> GetEnumerator() {
- object[] keyValues = this.instance as object[];
- if (keyValues != null) {
- return this.factory.ExecuteKeys(keyValues);
- }
- return this.factory.Execute(this.instance);
- }
- IEnumerator IEnumerable.GetEnumerator() {
- return this.GetEnumerator();
- }
- }
- }
- /// <summary>
- /// Returns true if any objects have been added to the identity cache. If
- /// object tracking is disabled, this still returns true if any attempts
- /// where made to cache an object. Thus regardless of object tracking mode,
- /// this can be used as an indicator as to whether any result returning queries
- /// have been executed.
- /// </summary>
- internal bool HasCachedObjects {
- get {
- return this.hasCachedObjects;
- }
- }
- public object GetCachedObject(Expression query) {
- if (query == null)
- return null;
- MethodCallExpression mc = query as MethodCallExpression;
- if (mc == null || mc.Arguments.Count < 1 || mc.Arguments.Count > 2)
- return null;
- if (mc.Method.DeclaringType != typeof(Queryable)) {
- return null;
- }
- switch (mc.Method.Name) {
- case "Where":
- case "First":
- case "FirstOrDefault":
- case "Single":
- case "SingleOrDefault":
- break;
- default:
- return null;
- }
- if (mc.Arguments.Count == 1) {
- // If it is something like
- // context.Customers.Where(c => c.ID = 123).First()
- // then it is equivalent of
- // context.Customers.First(c => c.ID = 123)
- // hence reduce to context.Customers.Where(c => c.ID = 123) and process the remaining query
- return GetCachedObject(mc.Arguments[0]);
- }
- UnaryExpression quote = mc.Arguments[1] as UnaryExpression;
- if (quote == null || quote.NodeType != ExpressionType.Quote)
- return null;
- LambdaExpression pred = quote.Operand as LambdaExpression;
- if (pred == null)
- return null;
- ConstantExpression cex = mc.Arguments[0] as ConstantExpression;
- if (cex == null)
- return null;
- ITable t = cex.Value as ITable;
- if (t == null)
- return null;
- Type elementType = System.Data.Linq.SqlClient.TypeSystem.GetElementType(query.Type);
- if (elementType != t.ElementType)
- return null;
- MetaTable metaTable = this.metaModel.GetTable(t.ElementType);
- object[] keyValues = this.GetKeyValues(metaTable.RowType, pred);
- if (keyValues != null) {
- return this.GetCachedObject(metaTable.RowType, keyValues);
- }
- return null;
- }
- internal object[] GetKeyValues(MetaType type, LambdaExpression predicate) {
- if (predicate == null)
- throw Error.ArgumentNull("predicate");
- if (predicate.Parameters.Count != 1)
- return null;
- Dictionary<MetaDataMember, object> keys = new Dictionary<MetaDataMember, object>();
- if (this.GetKeysFromPredicate(type, keys, predicate.Body)
- && keys.Count == type.IdentityMembers.Count) {
- object[] values = keys.OrderBy(kv => kv.Key.Ordinal).Select(kv => kv.Value).ToArray();
- return values;
- }
- return null;
- }
- private bool GetKeysFromPredicate(MetaType type, Dictionary<MetaDataMember, object> keys, Expression expr) {
- BinaryExpression bex = expr as BinaryExpression;
- if (bex == null) {
- MethodCallExpression mex = expr as MethodCallExpression;
- if (mex != null && mex.Method.Name == "op_Equality" && mex.Arguments.Count == 2) {
- bex = Expression.Equal(mex.Arguments[0], mex.Arguments[1]);
- }
- else {
- return false;
- }
- }
- switch (bex.NodeType) {
- case ExpressionType.And:
- return this.GetKeysFromPredicate(type, keys, bex.Left) &&
- this.GetKeysFromPredicate(type, keys, bex.Right);
- case ExpressionType.Equal:
- return GetKeyFromPredicate(type, keys, bex.Left, bex.Right) ||
- GetKeyFromPredicate(type, keys, bex.Right, bex.Left);
- default:
- return false;
- }
- }
- private static bool GetKeyFromPredicate(MetaType type, Dictionary<MetaDataMember, object> keys, Expression mex, Expression vex) {
- MemberExpression memex = mex as MemberExpression;
- if (memex == null || memex.Expression == null ||
- memex.Expression.NodeType != ExpressionType.Parameter || memex.Expression.Type != type.Type) {
- return false;
- }
- if (!type.Type.IsAssignableFrom(memex.Member.ReflectedType) && !memex.Member.ReflectedType.IsAssignableFrom(type.Type)) {
- return false;
- }
- MetaDataMember mm = type.GetDataMember(memex.Member);
- if (!mm.IsPrimaryKey) {
- return false;
- }
- if (keys.ContainsKey(mm)) {
- return false;
- }
- ConstantExpression cex = vex as ConstantExpression;
- if (cex != null) {
- keys.Add(mm, cex.Value);
- return true;
- }
- InvocationExpression ie = vex as InvocationExpression;
- if (ie != null && ie.Arguments != null && ie.Arguments.Count == 0) {
- ConstantExpression ce = ie.Expression as ConstantExpression;
- if (ce != null) {
- keys.Add(mm, ((Delegate)ce.Value).DynamicInvoke(new object[] {}));
- return true;
- }
- }
- return false;
- }
- /// <summary>
- /// Either returns the object from cache if it is in cache, or
- /// queries for it.
- /// </summary>
- internal object GetObjectByKey(MetaType type, object[] keyValues) {
- // first check the cache
- object target = GetCachedObject(type, keyValues);
- if (target == null) {
- // no cached value, so query for it
- target = ((IEnumerable)this.context.Provider.Execute(this.GetObjectQuery(type, keyValues)).ReturnValue).OfType<object>().SingleOrDefault();
- }
- return target;
- }
- }
- internal struct RelatedItem {
- internal MetaType Type;
- internal object Item;
- internal RelatedItem(MetaType type, object item) {
- this.Type = type;
- this.Item = item;
- }
- }
- }
|