DataServices.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  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;
  10. namespace System.Data.Linq {
  11. using System.Data.Linq.Mapping;
  12. using System.Data.Linq.Provider;
  13. internal class CommonDataServices : IDataServices {
  14. DataContext context;
  15. MetaModel metaModel;
  16. IdentityManager identifier;
  17. ChangeTracker tracker;
  18. ChangeDirector director;
  19. bool hasCachedObjects;
  20. Dictionary<MetaDataMember, IDeferredSourceFactory> factoryMap;
  21. internal CommonDataServices(DataContext context, MetaModel model) {
  22. this.context = context;
  23. this.metaModel = model;
  24. bool asReadOnly = !context.ObjectTrackingEnabled;
  25. this.identifier = IdentityManager.CreateIdentityManager(asReadOnly);
  26. this.tracker = ChangeTracker.CreateChangeTracker(this, asReadOnly);
  27. this.director = ChangeDirector.CreateChangeDirector(context);
  28. this.factoryMap = new Dictionary<MetaDataMember, IDeferredSourceFactory>();
  29. }
  30. public DataContext Context {
  31. get { return this.context; }
  32. }
  33. public MetaModel Model {
  34. get { return this.metaModel; }
  35. }
  36. internal void SetModel(MetaModel model) {
  37. this.metaModel = model;
  38. }
  39. internal IdentityManager IdentityManager {
  40. get { return this.identifier; }
  41. }
  42. internal ChangeTracker ChangeTracker {
  43. get { return this.tracker; }
  44. }
  45. internal ChangeDirector ChangeDirector {
  46. get { return this.director; }
  47. }
  48. internal IEnumerable<RelatedItem> GetParents(MetaType type, object item) {
  49. return this.GetRelations(type, item, true);
  50. }
  51. internal IEnumerable<RelatedItem> GetChildren(MetaType type, object item) {
  52. return this.GetRelations(type, item, false);
  53. }
  54. private IEnumerable<RelatedItem> GetRelations(MetaType type, object item, bool isForeignKey) {
  55. foreach (MetaDataMember mm in type.PersistentDataMembers) {
  56. if (mm.IsAssociation) {
  57. MetaType otherType = mm.Association.OtherType;
  58. if (mm.Association.IsForeignKey == isForeignKey) {
  59. object value = null;
  60. if (mm.IsDeferred) {
  61. value = mm.DeferredValueAccessor.GetBoxedValue(item);
  62. }
  63. else {
  64. value = mm.StorageAccessor.GetBoxedValue(item);
  65. }
  66. if (value != null) {
  67. if (mm.Association.IsMany) {
  68. IEnumerable list = (IEnumerable)value;
  69. foreach (object otherItem in list) {
  70. yield return new RelatedItem(otherType.GetInheritanceType(otherItem.GetType()), otherItem);
  71. }
  72. }
  73. else {
  74. yield return new RelatedItem(otherType.GetInheritanceType(value.GetType()), value);
  75. }
  76. }
  77. }
  78. }
  79. }
  80. }
  81. internal void ResetServices() {
  82. hasCachedObjects = false;
  83. bool asReadOnly = !context.ObjectTrackingEnabled;
  84. this.identifier = IdentityManager.CreateIdentityManager(asReadOnly);
  85. this.tracker = ChangeTracker.CreateChangeTracker(this, asReadOnly);
  86. this.factoryMap = new Dictionary<MetaDataMember, IDeferredSourceFactory>();
  87. }
  88. internal static object[] GetKeyValues(MetaType type, object instance) {
  89. List<object> keyValues = new List<object>();
  90. foreach (MetaDataMember mm in type.IdentityMembers) {
  91. keyValues.Add(mm.MemberAccessor.GetBoxedValue(instance));
  92. }
  93. return keyValues.ToArray();
  94. }
  95. internal static object[] GetForeignKeyValues(MetaAssociation association, object instance) {
  96. List<object> keyValues = new List<object>();
  97. foreach(MetaDataMember mm in association.ThisKey) {
  98. keyValues.Add(mm.MemberAccessor.GetBoxedValue(instance));
  99. }
  100. return keyValues.ToArray();
  101. }
  102. internal object GetCachedObject(MetaType type, object[] keyValues) {
  103. if( type == null ) {
  104. throw Error.ArgumentNull("type");
  105. }
  106. if (!type.IsEntity) {
  107. return null;
  108. }
  109. return this.identifier.Find(type, keyValues);
  110. }
  111. internal object GetCachedObjectLike(MetaType type, object instance) {
  112. if( type == null ) {
  113. throw Error.ArgumentNull("type");
  114. }
  115. if (!type.IsEntity) {
  116. return null;
  117. }
  118. return this.identifier.FindLike(type, instance);
  119. }
  120. public bool IsCachedObject(MetaType type, object instance) {
  121. if( type == null ) {
  122. throw Error.ArgumentNull("type");
  123. }
  124. if (!type.IsEntity) {
  125. return false;
  126. }
  127. return this.identifier.FindLike(type, instance) == instance;
  128. }
  129. public object InsertLookupCachedObject(MetaType type, object instance) {
  130. if( type == null ) {
  131. throw Error.ArgumentNull("type");
  132. }
  133. hasCachedObjects = true; // flag that we have cached objects
  134. if (!type.IsEntity) {
  135. return instance;
  136. }
  137. return this.identifier.InsertLookup(type, instance);
  138. }
  139. public bool RemoveCachedObjectLike(MetaType type, object instance) {
  140. if (type == null) {
  141. throw Error.ArgumentNull("type");
  142. }
  143. if (!type.IsEntity) {
  144. return false;
  145. }
  146. return this.identifier.RemoveLike(type, instance);
  147. }
  148. public void OnEntityMaterialized(MetaType type, object instance) {
  149. if (type == null) {
  150. throw Error.ArgumentNull("type");
  151. }
  152. this.tracker.FastTrack(instance);
  153. if (type.HasAnyLoadMethod) {
  154. SendOnLoaded(type, instance);
  155. }
  156. }
  157. private static void SendOnLoaded(MetaType type, object item) {
  158. if (type != null) {
  159. SendOnLoaded(type.InheritanceBase, item);
  160. if (type.OnLoadedMethod != null) {
  161. try {
  162. type.OnLoadedMethod.Invoke(item, new object[] { });
  163. } catch (TargetInvocationException tie) {
  164. if (tie.InnerException != null) {
  165. throw tie.InnerException;
  166. }
  167. throw;
  168. }
  169. }
  170. }
  171. }
  172. /// <summary>
  173. /// Returns a query for the entity indicated by the specified key.
  174. /// </summary>
  175. internal Expression GetObjectQuery(MetaType type, object[] keyValues) {
  176. if (type == null) {
  177. throw Error.ArgumentNull("type");
  178. }
  179. if (keyValues == null) {
  180. throw Error.ArgumentNull("keyValues");
  181. }
  182. return this.GetObjectQuery(type, BuildKeyExpressions(keyValues, type.IdentityMembers));
  183. }
  184. internal Expression GetObjectQuery(MetaType type, Expression[] keyValues) {
  185. ITable table = this.context.GetTable(type.InheritanceRoot.Type);
  186. ParameterExpression serverItem = Expression.Parameter(table.ElementType, "p");
  187. // create a where expression including all the identity members
  188. Expression whereExpression = null;
  189. for (int i = 0, n = type.IdentityMembers.Count; i < n; i++) {
  190. MetaDataMember metaMember = type.IdentityMembers[i];
  191. Expression memberExpression = (metaMember.Member is FieldInfo)
  192. ? Expression.Field(serverItem, (FieldInfo)metaMember.Member)
  193. : Expression.Property(serverItem, (PropertyInfo)metaMember.Member);
  194. Expression memberEqualityExpression = Expression.Equal(memberExpression, keyValues[i]);
  195. whereExpression = (whereExpression != null)
  196. ? Expression.And(whereExpression, memberEqualityExpression)
  197. : memberEqualityExpression;
  198. }
  199. return Expression.Call(typeof(Queryable), "Where", new Type[] { table.ElementType }, table.Expression, Expression.Lambda(whereExpression, serverItem));
  200. }
  201. internal Expression GetDataMemberQuery(MetaDataMember member, Expression[] keyValues) {
  202. if (member == null)
  203. throw Error.ArgumentNull("member");
  204. if (keyValues == null)
  205. throw Error.ArgumentNull("keyValues");
  206. if (member.IsAssociation) {
  207. MetaAssociation association = member.Association;
  208. Type rootType = association.ThisMember.DeclaringType.InheritanceRoot.Type;
  209. Expression thisSource = Expression.Constant(context.GetTable(rootType));
  210. if (rootType != association.ThisMember.DeclaringType.Type) {
  211. thisSource = Expression.Call(typeof(Enumerable), "Cast", new Type[] { association.ThisMember.DeclaringType.Type }, thisSource);
  212. }
  213. Expression thisInstance = Expression.Call(typeof(Enumerable), "FirstOrDefault", new Type[] { association.ThisMember.DeclaringType.Type },
  214. System.Data.Linq.SqlClient.Translator.WhereClauseFromSourceAndKeys(thisSource, association.ThisKey.ToArray(), keyValues)
  215. );
  216. Expression otherSource = Expression.Constant(context.GetTable(association.OtherType.InheritanceRoot.Type));
  217. if (association.OtherType.Type!=association.OtherType.InheritanceRoot.Type) {
  218. otherSource = Expression.Call(typeof(Enumerable), "Cast", new Type[] { association.OtherType.Type }, otherSource);
  219. }
  220. Expression expr = System.Data.Linq.SqlClient.Translator.TranslateAssociation(
  221. this.context, association, otherSource, keyValues, thisInstance
  222. );
  223. return expr;
  224. }
  225. else {
  226. Expression query = this.GetObjectQuery(member.DeclaringType, keyValues);
  227. Type elementType = System.Data.Linq.SqlClient.TypeSystem.GetElementType(query.Type);
  228. ParameterExpression p = Expression.Parameter(elementType, "p");
  229. Expression e = p;
  230. if (elementType != member.DeclaringType.Type)
  231. e = Expression.Convert(e, member.DeclaringType.Type);
  232. Expression mem = (member.Member is PropertyInfo)
  233. ? Expression.Property(e, (PropertyInfo)member.Member)
  234. : Expression.Field(e, (FieldInfo)member.Member);
  235. LambdaExpression selector = Expression.Lambda(mem, p);
  236. return Expression.Call(typeof(Queryable), "Select", new Type[] { elementType, selector.Body.Type }, query, selector);
  237. }
  238. }
  239. private static Expression[] BuildKeyExpressions(object[] keyValues, ReadOnlyCollection<MetaDataMember> keyMembers) {
  240. Expression[] keyValueExpressions = new Expression[keyValues.Length];
  241. for (int i = 0, n = keyMembers.Count; i < n; i++) {
  242. MetaDataMember metaMember = keyMembers[i];
  243. Expression keyValueExpression = Expression.Constant(keyValues[i], metaMember.Type);
  244. keyValueExpressions[i] = keyValueExpression;
  245. }
  246. return keyValueExpressions;
  247. }
  248. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  249. public IDeferredSourceFactory GetDeferredSourceFactory(MetaDataMember member) {
  250. if (member == null) {
  251. throw Error.ArgumentNull("member");
  252. }
  253. IDeferredSourceFactory factory;
  254. if (this.factoryMap.TryGetValue(member, out factory)) {
  255. return factory;
  256. }
  257. Type elemType = member.IsAssociation && member.Association.IsMany
  258. ? System.Data.Linq.SqlClient.TypeSystem.GetElementType(member.Type)
  259. : member.Type;
  260. factory = (IDeferredSourceFactory) Activator.CreateInstance(
  261. typeof(DeferredSourceFactory<>).MakeGenericType(elemType),
  262. BindingFlags.Instance | BindingFlags.NonPublic, null,
  263. new object[] { member, this }, null
  264. );
  265. this.factoryMap.Add(member, factory);
  266. return factory;
  267. }
  268. class DeferredSourceFactory<T> : IDeferredSourceFactory {
  269. MetaDataMember member;
  270. CommonDataServices services;
  271. ICompiledQuery query;
  272. bool refersToPrimaryKey;
  273. T[] empty;
  274. internal DeferredSourceFactory(MetaDataMember member, CommonDataServices services) {
  275. this.member = member;
  276. this.services = services;
  277. this.refersToPrimaryKey = this.member.IsAssociation && this.member.Association.OtherKeyIsPrimaryKey;
  278. this.empty = new T[] { };
  279. }
  280. public IEnumerable CreateDeferredSource(object instance) {
  281. if (instance == null)
  282. throw Error.ArgumentNull("instance");
  283. return new DeferredSource(this, instance);
  284. }
  285. public IEnumerable CreateDeferredSource(object[] keyValues) {
  286. if (keyValues == null)
  287. throw Error.ArgumentNull("keyValues");
  288. return new DeferredSource(this, keyValues);
  289. }
  290. private IEnumerator<T> Execute(object instance) {
  291. ReadOnlyCollection<MetaDataMember> keys = null;
  292. if (this.member.IsAssociation) {
  293. keys = this.member.Association.ThisKey;
  294. }
  295. else {
  296. keys = this.member.DeclaringType.IdentityMembers;
  297. }
  298. object[] keyValues = new object[keys.Count];
  299. for (int i = 0, n = keys.Count; i < n; i++) {
  300. object value = keys[i].StorageAccessor.GetBoxedValue(instance);
  301. keyValues[i] = value;
  302. }
  303. if (this.HasNullForeignKey(keyValues)) {
  304. return ((IEnumerable<T>)this.empty).GetEnumerator();
  305. }
  306. T cached;
  307. if (this.TryGetCachedObject(keyValues, out cached)) {
  308. return ((IEnumerable<T>)(new T[] { cached })).GetEnumerator();
  309. }
  310. if (this.member.LoadMethod != null) {
  311. try {
  312. object result = this.member.LoadMethod.Invoke(this.services.Context, new object[] { instance });
  313. if (typeof(T).IsAssignableFrom(this.member.LoadMethod.ReturnType)) {
  314. return ((IEnumerable<T>)new T[] { (T)result }).GetEnumerator();
  315. }
  316. else {
  317. return ((IEnumerable<T>)result).GetEnumerator();
  318. }
  319. }
  320. catch (TargetInvocationException tie) {
  321. if (tie.InnerException != null) {
  322. throw tie.InnerException;
  323. }
  324. throw;
  325. }
  326. }
  327. else {
  328. return this.ExecuteKeyQuery(keyValues);
  329. }
  330. }
  331. private IEnumerator<T> ExecuteKeys(object[] keyValues) {
  332. if (this.HasNullForeignKey(keyValues)) {
  333. return ((IEnumerable<T>)this.empty).GetEnumerator();
  334. }
  335. T cached;
  336. if (this.TryGetCachedObject(keyValues, out cached)) {
  337. return ((IEnumerable<T>)(new T[] { cached })).GetEnumerator();
  338. }
  339. return this.ExecuteKeyQuery(keyValues);
  340. }
  341. private bool HasNullForeignKey(object[] keyValues) {
  342. if (this.refersToPrimaryKey) {
  343. bool keyHasNull = false;
  344. for (int i = 0, n = keyValues.Length; i < n; i++) {
  345. keyHasNull |= keyValues[i] == null;
  346. }
  347. if (keyHasNull) {
  348. return true;
  349. }
  350. }
  351. return false;
  352. }
  353. private bool TryGetCachedObject(object[] keyValues, out T cached) {
  354. cached = default(T);
  355. if (this.refersToPrimaryKey) {
  356. // look to see if we already have this object in the identity cache
  357. MetaType mt = this.member.IsAssociation ? this.member.Association.OtherType : this.member.DeclaringType;
  358. object obj = this.services.GetCachedObject(mt, keyValues);
  359. if (obj != null) {
  360. cached = (T)obj;
  361. return true;
  362. }
  363. }
  364. return false;
  365. }
  366. private IEnumerator<T> ExecuteKeyQuery(object[] keyValues) {
  367. if (this.query == null) {
  368. ParameterExpression p = Expression.Parameter(typeof(object[]), "keys");
  369. Expression[] keyExprs = new Expression[keyValues.Length];
  370. ReadOnlyCollection<MetaDataMember> members = this.member.IsAssociation ? this.member.Association.OtherKey : this.member.DeclaringType.IdentityMembers;
  371. for (int i = 0, n = keyValues.Length; i < n; i++) {
  372. MetaDataMember mm = members[i];
  373. keyExprs[i] = Expression.Convert(
  374. #pragma warning disable 618 // Disable the 'obsolete' warning
  375. Expression.ArrayIndex(p, Expression.Constant(i)),
  376. #pragma warning restore 618
  377. mm.Type
  378. );
  379. }
  380. Expression q = this.services.GetDataMemberQuery(this.member, keyExprs);
  381. LambdaExpression lambda = Expression.Lambda(q, p);
  382. this.query = this.services.Context.Provider.Compile(lambda);
  383. }
  384. return ((IEnumerable<T>)this.query.Execute(this.services.Context.Provider, new object[] { keyValues }).ReturnValue).GetEnumerator();
  385. }
  386. class DeferredSource : IEnumerable<T>, IEnumerable {
  387. DeferredSourceFactory<T> factory;
  388. object instance;
  389. internal DeferredSource(DeferredSourceFactory<T> factory, object instance) {
  390. this.factory = factory;
  391. this.instance = instance;
  392. }
  393. public IEnumerator<T> GetEnumerator() {
  394. object[] keyValues = this.instance as object[];
  395. if (keyValues != null) {
  396. return this.factory.ExecuteKeys(keyValues);
  397. }
  398. return this.factory.Execute(this.instance);
  399. }
  400. IEnumerator IEnumerable.GetEnumerator() {
  401. return this.GetEnumerator();
  402. }
  403. }
  404. }
  405. /// <summary>
  406. /// Returns true if any objects have been added to the identity cache. If
  407. /// object tracking is disabled, this still returns true if any attempts
  408. /// where made to cache an object. Thus regardless of object tracking mode,
  409. /// this can be used as an indicator as to whether any result returning queries
  410. /// have been executed.
  411. /// </summary>
  412. internal bool HasCachedObjects {
  413. get {
  414. return this.hasCachedObjects;
  415. }
  416. }
  417. public object GetCachedObject(Expression query) {
  418. if (query == null)
  419. return null;
  420. MethodCallExpression mc = query as MethodCallExpression;
  421. if (mc == null || mc.Arguments.Count < 1 || mc.Arguments.Count > 2)
  422. return null;
  423. if (mc.Method.DeclaringType != typeof(Queryable)) {
  424. return null;
  425. }
  426. switch (mc.Method.Name) {
  427. case "Where":
  428. case "First":
  429. case "FirstOrDefault":
  430. case "Single":
  431. case "SingleOrDefault":
  432. break;
  433. default:
  434. return null;
  435. }
  436. if (mc.Arguments.Count == 1) {
  437. // If it is something like
  438. // context.Customers.Where(c => c.ID = 123).First()
  439. // then it is equivalent of
  440. // context.Customers.First(c => c.ID = 123)
  441. // hence reduce to context.Customers.Where(c => c.ID = 123) and process the remaining query
  442. return GetCachedObject(mc.Arguments[0]);
  443. }
  444. UnaryExpression quote = mc.Arguments[1] as UnaryExpression;
  445. if (quote == null || quote.NodeType != ExpressionType.Quote)
  446. return null;
  447. LambdaExpression pred = quote.Operand as LambdaExpression;
  448. if (pred == null)
  449. return null;
  450. ConstantExpression cex = mc.Arguments[0] as ConstantExpression;
  451. if (cex == null)
  452. return null;
  453. ITable t = cex.Value as ITable;
  454. if (t == null)
  455. return null;
  456. Type elementType = System.Data.Linq.SqlClient.TypeSystem.GetElementType(query.Type);
  457. if (elementType != t.ElementType)
  458. return null;
  459. MetaTable metaTable = this.metaModel.GetTable(t.ElementType);
  460. object[] keyValues = this.GetKeyValues(metaTable.RowType, pred);
  461. if (keyValues != null) {
  462. return this.GetCachedObject(metaTable.RowType, keyValues);
  463. }
  464. return null;
  465. }
  466. internal object[] GetKeyValues(MetaType type, LambdaExpression predicate) {
  467. if (predicate == null)
  468. throw Error.ArgumentNull("predicate");
  469. if (predicate.Parameters.Count != 1)
  470. return null;
  471. Dictionary<MetaDataMember, object> keys = new Dictionary<MetaDataMember, object>();
  472. if (this.GetKeysFromPredicate(type, keys, predicate.Body)
  473. && keys.Count == type.IdentityMembers.Count) {
  474. object[] values = keys.OrderBy(kv => kv.Key.Ordinal).Select(kv => kv.Value).ToArray();
  475. return values;
  476. }
  477. return null;
  478. }
  479. private bool GetKeysFromPredicate(MetaType type, Dictionary<MetaDataMember, object> keys, Expression expr) {
  480. BinaryExpression bex = expr as BinaryExpression;
  481. if (bex == null) {
  482. MethodCallExpression mex = expr as MethodCallExpression;
  483. if (mex != null && mex.Method.Name == "op_Equality" && mex.Arguments.Count == 2) {
  484. bex = Expression.Equal(mex.Arguments[0], mex.Arguments[1]);
  485. }
  486. else {
  487. return false;
  488. }
  489. }
  490. switch (bex.NodeType) {
  491. case ExpressionType.And:
  492. return this.GetKeysFromPredicate(type, keys, bex.Left) &&
  493. this.GetKeysFromPredicate(type, keys, bex.Right);
  494. case ExpressionType.Equal:
  495. return GetKeyFromPredicate(type, keys, bex.Left, bex.Right) ||
  496. GetKeyFromPredicate(type, keys, bex.Right, bex.Left);
  497. default:
  498. return false;
  499. }
  500. }
  501. private static bool GetKeyFromPredicate(MetaType type, Dictionary<MetaDataMember, object> keys, Expression mex, Expression vex) {
  502. MemberExpression memex = mex as MemberExpression;
  503. if (memex == null || memex.Expression == null ||
  504. memex.Expression.NodeType != ExpressionType.Parameter || memex.Expression.Type != type.Type) {
  505. return false;
  506. }
  507. if (!type.Type.IsAssignableFrom(memex.Member.ReflectedType) && !memex.Member.ReflectedType.IsAssignableFrom(type.Type)) {
  508. return false;
  509. }
  510. MetaDataMember mm = type.GetDataMember(memex.Member);
  511. if (!mm.IsPrimaryKey) {
  512. return false;
  513. }
  514. if (keys.ContainsKey(mm)) {
  515. return false;
  516. }
  517. ConstantExpression cex = vex as ConstantExpression;
  518. if (cex != null) {
  519. keys.Add(mm, cex.Value);
  520. return true;
  521. }
  522. InvocationExpression ie = vex as InvocationExpression;
  523. if (ie != null && ie.Arguments != null && ie.Arguments.Count == 0) {
  524. ConstantExpression ce = ie.Expression as ConstantExpression;
  525. if (ce != null) {
  526. keys.Add(mm, ((Delegate)ce.Value).DynamicInvoke(new object[] {}));
  527. return true;
  528. }
  529. }
  530. return false;
  531. }
  532. /// <summary>
  533. /// Either returns the object from cache if it is in cache, or
  534. /// queries for it.
  535. /// </summary>
  536. internal object GetObjectByKey(MetaType type, object[] keyValues) {
  537. // first check the cache
  538. object target = GetCachedObject(type, keyValues);
  539. if (target == null) {
  540. // no cached value, so query for it
  541. target = ((IEnumerable)this.context.Provider.Execute(this.GetObjectQuery(type, keyValues)).ReturnValue).OfType<object>().SingleOrDefault();
  542. }
  543. return target;
  544. }
  545. }
  546. internal struct RelatedItem {
  547. internal MetaType Type;
  548. internal object Item;
  549. internal RelatedItem(MetaType type, object item) {
  550. this.Type = type;
  551. this.Item = item;
  552. }
  553. }
  554. }