PageRenderTime 57ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/System.Data.Services.Client/Client/System/Data/Services/Client/AtomMaterializer.cs

https://bitbucket.org/danipen/mono
C# | 1511 lines | 1269 code | 232 blank | 10 comment | 361 complexity | 8784fa3349e5b3c1c890d3dcaf0f3018 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //Copyright 2010 Microsoft Corporation
  2. //
  3. //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
  4. //You may obtain a copy of the License at
  5. //
  6. //http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. //Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
  9. //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. //See the License for the specific language governing permissions and limitations under the License.
  11. namespace System.Data.Services.Client
  12. {
  13. #region Namespaces.
  14. using System;
  15. using System.Collections;
  16. using System.Collections.Generic;
  17. using System.Data.Services.Common;
  18. using System.Diagnostics;
  19. using System.Linq;
  20. using System.Linq.Expressions;
  21. using System.Reflection;
  22. using System.Xml;
  23. using System.Xml.Linq;
  24. #endregion Namespaces.
  25. internal static class AtomMaterializerInvoker
  26. {
  27. internal static IEnumerable<T> EnumerateAsElementType<T>(IEnumerable source)
  28. {
  29. return AtomMaterializer.EnumerateAsElementType<T>(source);
  30. }
  31. internal static List<TTarget> ListAsElementType<T, TTarget>(object materializer, IEnumerable<T> source) where T : TTarget
  32. {
  33. Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
  34. return AtomMaterializer.ListAsElementType<T, TTarget>((AtomMaterializer)materializer, source);
  35. }
  36. internal static bool ProjectionCheckValueForPathIsNull(
  37. object entry,
  38. Type expectedType,
  39. object path)
  40. {
  41. Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
  42. Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
  43. return AtomMaterializer.ProjectionCheckValueForPathIsNull((AtomEntry)entry, expectedType, (ProjectionPath)path);
  44. }
  45. internal static IEnumerable ProjectionSelect(
  46. object materializer,
  47. object entry,
  48. Type expectedType,
  49. Type resultType,
  50. object path,
  51. Func<object, object, Type, object> selector)
  52. {
  53. Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
  54. Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
  55. Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
  56. return AtomMaterializer.ProjectionSelect((AtomMaterializer)materializer, (AtomEntry)entry, expectedType, resultType, (ProjectionPath)path, selector);
  57. }
  58. internal static AtomEntry ProjectionGetEntry(object entry, string name)
  59. {
  60. Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
  61. return AtomMaterializer.ProjectionGetEntry((AtomEntry)entry, name);
  62. }
  63. internal static object ProjectionInitializeEntity(
  64. object materializer,
  65. object entry,
  66. Type expectedType,
  67. Type resultType,
  68. string[] properties,
  69. Func<object, object, Type, object>[] propertyValues)
  70. {
  71. Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
  72. Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
  73. return AtomMaterializer.ProjectionInitializeEntity((AtomMaterializer)materializer, (AtomEntry)entry, expectedType, resultType, properties, propertyValues);
  74. }
  75. internal static object ProjectionValueForPath(object materializer, object entry, Type expectedType, object path)
  76. {
  77. Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
  78. Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
  79. Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
  80. return AtomMaterializer.ProjectionValueForPath((AtomMaterializer)materializer, (AtomEntry)entry, expectedType, (ProjectionPath)path);
  81. }
  82. internal static object DirectMaterializePlan(object materializer, object entry, Type expectedEntryType)
  83. {
  84. Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
  85. Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
  86. return AtomMaterializer.DirectMaterializePlan((AtomMaterializer)materializer, (AtomEntry)entry, expectedEntryType);
  87. }
  88. internal static object ShallowMaterializePlan(object materializer, object entry, Type expectedEntryType)
  89. {
  90. Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
  91. Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
  92. return AtomMaterializer.ShallowMaterializePlan((AtomMaterializer)materializer, (AtomEntry)entry, expectedEntryType);
  93. }
  94. }
  95. [DebuggerDisplay("AtomMaterializer {parser}")]
  96. internal class AtomMaterializer
  97. {
  98. #region Private fields.
  99. private readonly DataServiceContext context;
  100. private readonly Type expectedType;
  101. private readonly AtomMaterializerLog log;
  102. private readonly ProjectionPlan materializeEntryPlan;
  103. private readonly Action<object, object> materializedObjectCallback;
  104. private readonly MergeOption mergeOption;
  105. private readonly Dictionary<IEnumerable, DataServiceQueryContinuation> nextLinkTable;
  106. private readonly AtomParser parser;
  107. private object currentValue;
  108. private bool ignoreMissingProperties;
  109. private object targetInstance;
  110. #endregion Private fields.
  111. #region Constructors.
  112. internal AtomMaterializer(
  113. AtomParser parser,
  114. DataServiceContext context,
  115. Type expectedType,
  116. bool ignoreMissingProperties,
  117. MergeOption mergeOption,
  118. AtomMaterializerLog log,
  119. Action<object, object> materializedObjectCallback,
  120. QueryComponents queryComponents,
  121. ProjectionPlan plan)
  122. {
  123. Debug.Assert(context != null, "context != null");
  124. Debug.Assert(parser != null, "parser != null");
  125. Debug.Assert(log != null, "log != null");
  126. this.context = context;
  127. this.parser = parser;
  128. this.expectedType = expectedType;
  129. this.ignoreMissingProperties = ignoreMissingProperties;
  130. this.mergeOption = mergeOption;
  131. this.log = log;
  132. this.materializedObjectCallback = materializedObjectCallback;
  133. this.nextLinkTable = new Dictionary<IEnumerable, DataServiceQueryContinuation>(ReferenceEqualityComparer<IEnumerable>.Instance);
  134. this.materializeEntryPlan = plan ?? CreatePlan(queryComponents);
  135. }
  136. #endregion Constructors.
  137. #region Internal properties.
  138. internal DataServiceContext Context
  139. {
  140. get { return this.context; }
  141. }
  142. internal ProjectionPlan MaterializeEntryPlan
  143. {
  144. get { return this.materializeEntryPlan; }
  145. }
  146. internal object TargetInstance
  147. {
  148. get
  149. {
  150. return this.targetInstance;
  151. }
  152. set
  153. {
  154. Debug.Assert(value != null, "value != null -- otherwise we have no instance target.");
  155. this.targetInstance = value;
  156. }
  157. }
  158. internal AtomFeed CurrentFeed
  159. {
  160. get
  161. {
  162. return this.parser.CurrentFeed;
  163. }
  164. }
  165. internal AtomEntry CurrentEntry
  166. {
  167. get
  168. {
  169. return this.parser.CurrentEntry;
  170. }
  171. }
  172. internal object CurrentValue
  173. {
  174. get
  175. {
  176. return this.currentValue;
  177. }
  178. }
  179. internal AtomMaterializerLog Log
  180. {
  181. get { return this.log; }
  182. }
  183. internal Dictionary<IEnumerable, DataServiceQueryContinuation> NextLinkTable
  184. {
  185. get { return this.nextLinkTable; }
  186. }
  187. internal bool IsEndOfStream
  188. {
  189. get { return this.parser.DataKind == AtomDataKind.Finished; }
  190. }
  191. #endregion Internal properties.
  192. #region Projection support.
  193. internal static IEnumerable<T> EnumerateAsElementType<T>(IEnumerable source)
  194. {
  195. Debug.Assert(source != null, "source != null");
  196. IEnumerable<T> typedSource = source as IEnumerable<T>;
  197. if (typedSource != null)
  198. {
  199. return typedSource;
  200. }
  201. else
  202. {
  203. return EnumerateAsElementTypeInternal<T>(source);
  204. }
  205. }
  206. internal static IEnumerable<T> EnumerateAsElementTypeInternal<T>(IEnumerable source)
  207. {
  208. Debug.Assert(source != null, "source != null");
  209. foreach (object item in source)
  210. {
  211. yield return (T)item;
  212. }
  213. }
  214. internal static bool IsDataServiceCollection(Type type)
  215. {
  216. while (type != null)
  217. {
  218. if (type.IsGenericType && WebUtil.IsDataServiceCollectionType(type.GetGenericTypeDefinition()))
  219. {
  220. return true;
  221. }
  222. type = type.BaseType;
  223. }
  224. return false;
  225. }
  226. internal static List<TTarget> ListAsElementType<T, TTarget>(AtomMaterializer materializer, IEnumerable<T> source) where T : TTarget
  227. {
  228. Debug.Assert(materializer != null, "materializer != null");
  229. Debug.Assert(source != null, "source != null");
  230. List<TTarget> typedSource = source as List<TTarget>;
  231. if (typedSource != null)
  232. {
  233. return typedSource;
  234. }
  235. List<TTarget> list;
  236. IList sourceList = source as IList;
  237. if (sourceList != null)
  238. {
  239. list = new List<TTarget>(sourceList.Count);
  240. }
  241. else
  242. {
  243. list = new List<TTarget>();
  244. }
  245. foreach (T item in source)
  246. {
  247. list.Add((TTarget)item);
  248. }
  249. DataServiceQueryContinuation continuation;
  250. if (materializer.nextLinkTable.TryGetValue(source, out continuation))
  251. {
  252. materializer.nextLinkTable[list] = continuation;
  253. }
  254. return list;
  255. }
  256. internal static bool ProjectionCheckValueForPathIsNull(
  257. AtomEntry entry,
  258. Type expectedType,
  259. ProjectionPath path)
  260. {
  261. Debug.Assert(entry != null, "entry != null");
  262. Debug.Assert(path != null, "path != null");
  263. if (path.Count == 0 || path.Count == 1 && path[0].Member == null)
  264. {
  265. return entry.IsNull;
  266. }
  267. bool result = false;
  268. AtomContentProperty atomProperty = null;
  269. List<AtomContentProperty> properties = entry.DataValues;
  270. for (int i = 0; i < path.Count; i++)
  271. {
  272. var segment = path[i];
  273. if (segment.Member == null)
  274. {
  275. continue;
  276. }
  277. bool segmentIsLeaf = i == path.Count - 1;
  278. string propertyName = segment.Member;
  279. ClientType.ClientProperty property = ClientType.Create(expectedType).GetProperty(propertyName, false);
  280. atomProperty = GetPropertyOrThrow(properties, propertyName);
  281. ValidatePropertyMatch(property, atomProperty);
  282. if (atomProperty.Feed != null)
  283. {
  284. Debug.Assert(segmentIsLeaf, "segmentIsLeaf -- otherwise the path generated traverses a feed, which should be disallowed");
  285. result = false;
  286. }
  287. else
  288. {
  289. Debug.Assert(
  290. atomProperty.Entry != null,
  291. "atomProperty.Entry != null -- otherwise a primitive property / complex type is being rewritte with a null check; this is only supported for entities and collection");
  292. if (segmentIsLeaf)
  293. {
  294. result = atomProperty.Entry.IsNull;
  295. }
  296. properties = atomProperty.Entry.DataValues;
  297. entry = atomProperty.Entry;
  298. }
  299. expectedType = property.PropertyType;
  300. }
  301. return result;
  302. }
  303. internal static IEnumerable ProjectionSelect(
  304. AtomMaterializer materializer,
  305. AtomEntry entry,
  306. Type expectedType,
  307. Type resultType,
  308. ProjectionPath path,
  309. Func<object, object, Type, object> selector)
  310. {
  311. ClientType entryType = entry.ActualType ?? ClientType.Create(expectedType);
  312. IEnumerable list = (IEnumerable)Util.ActivatorCreateInstance(typeof(List<>).MakeGenericType(resultType));
  313. AtomContentProperty atomProperty = null;
  314. ClientType.ClientProperty property = null;
  315. for (int i = 0; i < path.Count; i++)
  316. {
  317. var segment = path[i];
  318. if (segment.Member == null)
  319. {
  320. continue;
  321. }
  322. string propertyName = segment.Member;
  323. property = entryType.GetProperty(propertyName, false);
  324. atomProperty = GetPropertyOrThrow(entry, propertyName);
  325. if (atomProperty.Entry != null)
  326. {
  327. entry = atomProperty.Entry;
  328. entryType = ClientType.Create(property.NullablePropertyType, false);
  329. }
  330. }
  331. ValidatePropertyMatch(property, atomProperty);
  332. AtomFeed sourceFeed = atomProperty.Feed;
  333. Debug.Assert(
  334. sourceFeed != null,
  335. "sourceFeed != null -- otherwise ValidatePropertyMatch should have thrown or property isn't a collection (and should be part of this plan)");
  336. Action<object, object> addMethod = GetAddToCollectionDelegate(list.GetType());
  337. foreach (var paramEntry in sourceFeed.Entries)
  338. {
  339. object projected = selector(materializer, paramEntry, property.CollectionType );
  340. addMethod(list, projected);
  341. }
  342. ProjectionPlan plan = new ProjectionPlan();
  343. plan.LastSegmentType = property.CollectionType;
  344. plan.Plan = selector;
  345. plan.ProjectedType = resultType;
  346. materializer.FoundNextLinkForCollection(list, sourceFeed.NextLink, plan);
  347. return list;
  348. }
  349. internal static AtomEntry ProjectionGetEntry(AtomEntry entry, string name)
  350. {
  351. Debug.Assert(entry != null, "entry != null -- ProjectionGetEntry never returns a null entry, and top-level materialization shouldn't pass one in");
  352. AtomContentProperty property = GetPropertyOrThrow(entry, name);
  353. if (property.Entry == null)
  354. {
  355. throw new InvalidOperationException(Strings.AtomMaterializer_PropertyNotExpectedEntry(name, entry.Identity));
  356. }
  357. CheckEntryToAccessNotNull(property.Entry, name);
  358. return property.Entry;
  359. }
  360. internal static object ProjectionInitializeEntity(
  361. AtomMaterializer materializer,
  362. AtomEntry entry,
  363. Type expectedType,
  364. Type resultType,
  365. string[] properties,
  366. Func<object, object, Type, object>[] propertyValues)
  367. {
  368. if (entry == null || entry.IsNull)
  369. {
  370. throw new NullReferenceException(Strings.AtomMaterializer_EntryToInitializeIsNull(resultType.FullName));
  371. }
  372. object result;
  373. if (!entry.EntityHasBeenResolved)
  374. {
  375. AtomMaterializer.ProjectionEnsureEntryAvailableOfType(materializer, entry, resultType);
  376. }
  377. else if (!resultType.IsAssignableFrom(entry.ActualType.ElementType))
  378. {
  379. string message = Strings.AtomMaterializer_ProjectEntityTypeMismatch(
  380. resultType.FullName,
  381. entry.ActualType.ElementType.FullName,
  382. entry.Identity);
  383. throw new InvalidOperationException(message);
  384. }
  385. result = entry.ResolvedObject;
  386. for (int i = 0; i < properties.Length; i++)
  387. {
  388. var property = entry.ActualType.GetProperty(properties[i], materializer.ignoreMissingProperties);
  389. object value = propertyValues[i](materializer, entry, expectedType);
  390. if (entry.ShouldUpdateFromPayload && ClientType.Create(property.NullablePropertyType, false).IsEntityType)
  391. {
  392. materializer.Log.SetLink(entry, property.PropertyName, value);
  393. }
  394. bool isEntity = property.CollectionType == null || !ClientType.CheckElementTypeIsEntity(property.CollectionType);
  395. if (entry.ShouldUpdateFromPayload)
  396. {
  397. if (isEntity)
  398. {
  399. property.SetValue(result, value, property.PropertyName, false);
  400. }
  401. else
  402. {
  403. IEnumerable valueAsEnumerable = (IEnumerable)value;
  404. DataServiceQueryContinuation continuation = materializer.nextLinkTable[valueAsEnumerable];
  405. Uri nextLinkUri = continuation == null ? null : continuation.NextLinkUri;
  406. ProjectionPlan plan = continuation == null ? null : continuation.Plan;
  407. materializer.MergeLists(entry, property, valueAsEnumerable, nextLinkUri, plan);
  408. }
  409. }
  410. else if (!isEntity)
  411. {
  412. materializer.FoundNextLinkForUnmodifiedCollection(property.GetValue(entry.ResolvedObject) as IEnumerable);
  413. }
  414. }
  415. return result;
  416. }
  417. internal static object ProjectionValueForPath(AtomMaterializer materializer, AtomEntry entry, Type expectedType, ProjectionPath path)
  418. {
  419. Debug.Assert(materializer != null, "materializer != null");
  420. Debug.Assert(entry != null, "entry != null");
  421. Debug.Assert(path != null, "path != null");
  422. if (path.Count == 0 || path.Count == 1 && path[0].Member == null)
  423. {
  424. if (!entry.EntityHasBeenResolved)
  425. {
  426. materializer.Materialize(entry, expectedType, false);
  427. }
  428. return entry.ResolvedObject;
  429. }
  430. object result = null;
  431. AtomContentProperty atomProperty = null;
  432. List<AtomContentProperty> properties = entry.DataValues;
  433. for (int i = 0; i < path.Count; i++)
  434. {
  435. var segment = path[i];
  436. if (segment.Member == null)
  437. {
  438. continue;
  439. }
  440. bool segmentIsLeaf = i == path.Count - 1;
  441. string propertyName = segment.Member;
  442. if (segmentIsLeaf)
  443. {
  444. CheckEntryToAccessNotNull(entry, propertyName);
  445. if (!entry.EntityPropertyMappingsApplied)
  446. {
  447. ClientType attributeSourceType = MaterializeAtom.GetEntryClientType(entry.TypeName, materializer.context, expectedType, false);
  448. ApplyEntityPropertyMappings(entry, attributeSourceType);
  449. }
  450. }
  451. ClientType.ClientProperty property = ClientType.Create(expectedType).GetProperty(propertyName, false);
  452. atomProperty = GetPropertyOrThrow(properties, propertyName);
  453. ValidatePropertyMatch(property, atomProperty);
  454. AtomFeed feedValue = atomProperty.Feed;
  455. if (feedValue != null)
  456. {
  457. Debug.Assert(segmentIsLeaf, "segmentIsLeaf -- otherwise the path generated traverses a feed, which should be disallowed");
  458. Type collectionType = ClientType.GetImplementationType(segment.ProjectionType, typeof(ICollection<>));
  459. if (collectionType == null)
  460. {
  461. collectionType = ClientType.GetImplementationType(segment.ProjectionType, typeof(IEnumerable<>));
  462. }
  463. Debug.Assert(
  464. collectionType != null,
  465. "collectionType != null -- otherwise the property should never have been recognized as a collection");
  466. Type nestedExpectedType = collectionType.GetGenericArguments()[0];
  467. Type feedType = segment.ProjectionType;
  468. if (feedType.IsInterface || IsDataServiceCollection(feedType))
  469. {
  470. feedType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(nestedExpectedType);
  471. }
  472. IEnumerable list = (IEnumerable)Util.ActivatorCreateInstance(feedType);
  473. MaterializeToList(materializer, list, nestedExpectedType, feedValue.Entries);
  474. if (IsDataServiceCollection(segment.ProjectionType))
  475. {
  476. Type dataServiceCollectionType = WebUtil.GetDataServiceCollectionOfT(nestedExpectedType);
  477. list = (IEnumerable)Util.ActivatorCreateInstance(
  478. dataServiceCollectionType,
  479. list,
  480. TrackingMode.None); }
  481. ProjectionPlan plan = CreatePlanForShallowMaterialization(nestedExpectedType);
  482. materializer.FoundNextLinkForCollection(list, feedValue.NextLink, plan);
  483. result = list;
  484. }
  485. else if (atomProperty.Entry != null)
  486. {
  487. if (segmentIsLeaf && !atomProperty.Entry.EntityHasBeenResolved && !atomProperty.IsNull)
  488. {
  489. materializer.Materialize(atomProperty.Entry, property.PropertyType, false);
  490. }
  491. properties = atomProperty.Entry.DataValues;
  492. result = atomProperty.Entry.ResolvedObject;
  493. entry = atomProperty.Entry;
  494. }
  495. else
  496. {
  497. if (atomProperty.Properties != null)
  498. {
  499. if (atomProperty.MaterializedValue == null && !atomProperty.IsNull)
  500. {
  501. ClientType complexType = ClientType.Create(property.PropertyType);
  502. object complexInstance = Util.ActivatorCreateInstance(property.PropertyType);
  503. MaterializeDataValues(complexType, atomProperty.Properties, materializer.ignoreMissingProperties, materializer.context);
  504. ApplyDataValues(complexType, atomProperty.Properties, materializer.ignoreMissingProperties, materializer.context, complexInstance);
  505. atomProperty.MaterializedValue = complexInstance;
  506. }
  507. }
  508. else
  509. {
  510. MaterializeDataValue(property.NullablePropertyType, atomProperty, materializer.context);
  511. }
  512. properties = atomProperty.Properties;
  513. result = atomProperty.MaterializedValue;
  514. }
  515. expectedType = property.PropertyType;
  516. }
  517. return result;
  518. }
  519. internal static void ProjectionEnsureEntryAvailableOfType(AtomMaterializer materializer, AtomEntry entry, Type requiredType)
  520. {
  521. Debug.Assert(materializer != null, "materializer != null");
  522. Debug.Assert(entry != null, "entry != null");
  523. Debug.Assert(
  524. materializer.targetInstance == null,
  525. "materializer.targetInstance == null -- projection shouldn't have a target instance set; that's only used for POST replies");
  526. if (entry.EntityHasBeenResolved)
  527. {
  528. if (!requiredType.IsAssignableFrom(entry.ResolvedObject.GetType()))
  529. {
  530. throw new InvalidOperationException(
  531. "Expecting type '" + requiredType + "' for '" + entry.Identity + "', but found " +
  532. "a previously created instance of type '" + entry.ResolvedObject.GetType());
  533. }
  534. return;
  535. }
  536. if (entry.Identity == null)
  537. {
  538. throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement);
  539. }
  540. if (!materializer.TryResolveAsCreated(entry) &&
  541. !materializer.TryResolveFromContext(entry, requiredType))
  542. {
  543. materializer.ResolveByCreatingWithType(entry, requiredType);
  544. }
  545. else
  546. {
  547. if (!requiredType.IsAssignableFrom(entry.ResolvedObject.GetType()))
  548. {
  549. throw Error.InvalidOperation(Strings.Deserialize_Current(requiredType, entry.ResolvedObject.GetType()));
  550. }
  551. }
  552. }
  553. internal static object DirectMaterializePlan(AtomMaterializer materializer, AtomEntry entry, Type expectedEntryType)
  554. {
  555. materializer.Materialize(entry, expectedEntryType, true);
  556. return entry.ResolvedObject;
  557. }
  558. internal static object ShallowMaterializePlan(AtomMaterializer materializer, AtomEntry entry, Type expectedEntryType)
  559. {
  560. materializer.Materialize(entry, expectedEntryType, false);
  561. return entry.ResolvedObject;
  562. }
  563. internal static void ValidatePropertyMatch(ClientType.ClientProperty property, AtomContentProperty atomProperty)
  564. {
  565. Debug.Assert(property != null, "property != null");
  566. Debug.Assert(atomProperty != null, "atomProperty != null");
  567. if (property.IsKnownType && (atomProperty.Feed != null || atomProperty.Entry != null))
  568. {
  569. throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkLocalSimple);
  570. }
  571. if (atomProperty.Feed != null && property.CollectionType == null)
  572. {
  573. throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(property.PropertyName));
  574. }
  575. if (atomProperty.Entry != null && property.CollectionType != null)
  576. {
  577. throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(property.PropertyName));
  578. }
  579. }
  580. #endregion Projection support.
  581. #region Internal methods.
  582. internal bool Read()
  583. {
  584. this.currentValue = null;
  585. this.nextLinkTable.Clear();
  586. while (this.parser.Read())
  587. {
  588. Debug.Assert(
  589. this.parser.DataKind != AtomDataKind.None,
  590. "parser.DataKind != AtomDataKind.None -- otherwise parser.Read() didn't update its state");
  591. Debug.Assert(
  592. this.parser.DataKind != AtomDataKind.Finished,
  593. "parser.DataKind != AtomDataKind.Finished -- otherwise parser.Read() shouldn't have returned true");
  594. switch (this.parser.DataKind)
  595. {
  596. case AtomDataKind.Feed:
  597. case AtomDataKind.FeedCount:
  598. break;
  599. case AtomDataKind.Entry:
  600. Debug.Assert(
  601. this.parser.CurrentEntry != null,
  602. "parser.CurrentEntry != null -- otherwise parser.DataKind shouldn't be Entry");
  603. this.CurrentEntry.ResolvedObject = this.TargetInstance;
  604. this.currentValue = this.materializeEntryPlan.Run(this, this.CurrentEntry, this.expectedType);
  605. return true;
  606. case AtomDataKind.PagingLinks:
  607. break;
  608. default:
  609. Debug.Assert(
  610. this.parser.DataKind == AtomDataKind.Custom,
  611. "parser.DataKind == AtomDataKind.Custom -- otherwise AtomMaterializer.Read switch is missing a case");
  612. Type underlyingExpectedType = Nullable.GetUnderlyingType(this.expectedType) ?? this.expectedType;
  613. ClientType targetType = ClientType.Create(underlyingExpectedType);
  614. if (ClientConvert.IsKnownType(underlyingExpectedType))
  615. {
  616. string elementText = this.parser.ReadCustomElementString();
  617. if (elementText != null)
  618. {
  619. this.currentValue = ClientConvert.ChangeType(elementText, underlyingExpectedType);
  620. }
  621. return true;
  622. }
  623. else if (!targetType.IsEntityType && this.parser.IsDataWebElement)
  624. {
  625. AtomContentProperty property = this.parser.ReadCurrentPropertyValue();
  626. if (property == null || property.IsNull)
  627. {
  628. this.currentValue = null;
  629. }
  630. else
  631. {
  632. this.currentValue = targetType.CreateInstance();
  633. MaterializeDataValues(targetType, property.Properties, this.ignoreMissingProperties, this.context);
  634. ApplyDataValues(targetType, property.Properties, this.ignoreMissingProperties, this.context, this.currentValue);
  635. }
  636. return true;
  637. }
  638. break;
  639. }
  640. }
  641. Debug.Assert(this.parser.DataKind == AtomDataKind.Finished, "parser.DataKind == AtomDataKind.None");
  642. Debug.Assert(this.parser.CurrentEntry == null, "parser.Current == null");
  643. return false;
  644. }
  645. internal void PropagateContinuation<T>(IEnumerable<T> from, DataServiceCollection<T> to)
  646. {
  647. DataServiceQueryContinuation continuation;
  648. if (this.nextLinkTable.TryGetValue(from, out continuation))
  649. {
  650. this.nextLinkTable.Add(to, continuation);
  651. Util.SetNextLinkForCollection(to, continuation);
  652. }
  653. }
  654. #endregion Internal methods.
  655. #region Private methods.
  656. private static void CheckEntryToAccessNotNull(AtomEntry entry, string name)
  657. {
  658. Debug.Assert(entry != null, "entry != null");
  659. Debug.Assert(name != null, "name != null");
  660. if (entry.IsNull)
  661. {
  662. throw new NullReferenceException(Strings.AtomMaterializer_EntryToAccessIsNull(name));
  663. }
  664. }
  665. private static ProjectionPlan CreatePlan(QueryComponents queryComponents)
  666. {
  667. LambdaExpression projection = queryComponents.Projection;
  668. ProjectionPlan result;
  669. if (projection == null)
  670. {
  671. result = CreatePlanForDirectMaterialization(queryComponents.LastSegmentType);
  672. }
  673. else
  674. {
  675. result = ProjectionPlanCompiler.CompilePlan(projection, queryComponents.NormalizerRewrites);
  676. result.LastSegmentType = queryComponents.LastSegmentType;
  677. }
  678. return result;
  679. }
  680. private static ProjectionPlan CreatePlanForDirectMaterialization(Type lastSegmentType)
  681. {
  682. ProjectionPlan result = new ProjectionPlan();
  683. result.Plan = AtomMaterializerInvoker.DirectMaterializePlan;
  684. result.ProjectedType = lastSegmentType;
  685. result.LastSegmentType = lastSegmentType;
  686. return result;
  687. }
  688. private static ProjectionPlan CreatePlanForShallowMaterialization(Type lastSegmentType)
  689. {
  690. ProjectionPlan result = new ProjectionPlan();
  691. result.Plan = AtomMaterializerInvoker.ShallowMaterializePlan;
  692. result.ProjectedType = lastSegmentType;
  693. result.LastSegmentType = lastSegmentType;
  694. return result;
  695. }
  696. private static Action<object, object> GetAddToCollectionDelegate(Type listType)
  697. {
  698. Debug.Assert(listType != null, "listType != null");
  699. Type listElementType;
  700. MethodInfo addMethod = ClientType.GetAddToCollectionMethod(listType, out listElementType);
  701. ParameterExpression list = Expression.Parameter(typeof(object), "list");
  702. ParameterExpression item = Expression.Parameter(typeof(object), "element");
  703. Expression body = Expression.Call(Expression.Convert(list, listType), addMethod, Expression.Convert(item, listElementType));
  704. #if ASTORIA_LIGHT
  705. LambdaExpression lambda = ExpressionHelpers.CreateLambda(body, list, item);
  706. #else
  707. LambdaExpression lambda = Expression.Lambda(body, list, item);
  708. #endif
  709. return (Action<object, object>)lambda.Compile();
  710. }
  711. private static object GetOrCreateCollectionProperty(object instance, ClientType.ClientProperty property, Type collectionType)
  712. {
  713. Debug.Assert(instance != null, "instance != null");
  714. Debug.Assert(property != null, "property != null");
  715. Debug.Assert(property.CollectionType != null, "property.CollectionType != null -- otherwise property isn't a collection");
  716. object result;
  717. result = property.GetValue(instance);
  718. if (result == null)
  719. {
  720. if (collectionType == null)
  721. {
  722. collectionType = property.PropertyType;
  723. if (collectionType.IsInterface)
  724. {
  725. collectionType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(property.CollectionType);
  726. }
  727. }
  728. result = Activator.CreateInstance(collectionType);
  729. property.SetValue(instance, result, property.PropertyName, false );
  730. }
  731. Debug.Assert(result != null, "result != null -- otherwise GetOrCreateCollectionProperty didn't fall back to creation");
  732. return result;
  733. }
  734. private static void MaterializeToList(
  735. AtomMaterializer materializer,
  736. IEnumerable list,
  737. Type nestedExpectedType,
  738. IEnumerable<AtomEntry> entries)
  739. {
  740. Debug.Assert(materializer != null, "materializer != null");
  741. Debug.Assert(list != null, "list != null");
  742. Action<object, object> addMethod = GetAddToCollectionDelegate(list.GetType());
  743. foreach (AtomEntry feedEntry in entries)
  744. {
  745. if (!feedEntry.EntityHasBeenResolved)
  746. {
  747. materializer.Materialize(feedEntry, nestedExpectedType, false);
  748. }
  749. addMethod(list, feedEntry.ResolvedObject);
  750. }
  751. }
  752. private static bool MaterializeDataValue(Type type, AtomContentProperty atomProperty, DataServiceContext context)
  753. {
  754. Debug.Assert(type != null, "type != null");
  755. Debug.Assert(atomProperty != null, "atomProperty != null");
  756. Debug.Assert(context != null, "context != null");
  757. string propertyTypeName = atomProperty.TypeName;
  758. string propertyValueText = atomProperty.Text;
  759. ClientType nestedElementType = null;
  760. Type underlyingType = Nullable.GetUnderlyingType(type) ?? type;
  761. bool knownType = ClientConvert.IsKnownType(underlyingType);
  762. if (!knownType)
  763. {
  764. nestedElementType = MaterializeAtom.GetEntryClientType(propertyTypeName, context, type, true);
  765. Debug.Assert(nestedElementType != null, "nestedElementType != null -- otherwise ReadTypeAttribute (or someone!) should throw");
  766. knownType = ClientConvert.IsKnownType(nestedElementType.ElementType);
  767. }
  768. if (knownType)
  769. {
  770. if (atomProperty.IsNull)
  771. {
  772. if (!ClientType.CanAssignNull(type))
  773. {
  774. throw new InvalidOperationException(Strings.AtomMaterializer_CannotAssignNull(atomProperty.Name, type.FullName));
  775. }
  776. atomProperty.MaterializedValue = null;
  777. return true;
  778. }
  779. else
  780. {
  781. object value = propertyValueText;
  782. if (propertyValueText != null)
  783. {
  784. value = ClientConvert.ChangeType(propertyValueText, (null != nestedElementType ? nestedElementType.ElementType : underlyingType));
  785. }
  786. atomProperty.MaterializedValue = value;
  787. return true;
  788. }
  789. }
  790. return false;
  791. }
  792. private static void MaterializeDataValues(
  793. ClientType actualType,
  794. List<AtomContentProperty> values,
  795. bool ignoreMissingProperties,
  796. DataServiceContext context)
  797. {
  798. Debug.Assert(actualType != null, "actualType != null");
  799. Debug.Assert(values != null, "values != null");
  800. Debug.Assert(context != null, "context != null");
  801. foreach (var atomProperty in values)
  802. {
  803. string propertyName = atomProperty.Name;
  804. var property = actualType.GetProperty(propertyName, ignoreMissingProperties); // may throw
  805. if (property == null)
  806. {
  807. continue;
  808. }
  809. if (atomProperty.Feed == null && atomProperty.Entry == null)
  810. {
  811. bool materialized = MaterializeDataValue(property.NullablePropertyType, atomProperty, context);
  812. if (!materialized && property.CollectionType != null)
  813. {
  814. throw Error.NotSupported(Strings.ClientType_CollectionOfNonEntities);
  815. }
  816. }
  817. }
  818. }
  819. private static void ApplyDataValue(ClientType type, AtomContentProperty property, bool ignoreMissingProperties, DataServiceContext context, object instance)
  820. {
  821. Debug.Assert(type != null, "type != null");
  822. Debug.Assert(property != null, "property != null");
  823. Debug.Assert(context != null, "context != null");
  824. Debug.Assert(instance != null, "instance != context");
  825. var prop = type.GetProperty(property.Name, ignoreMissingProperties);
  826. if (prop == null)
  827. {
  828. return;
  829. }
  830. if (property.Properties != null)
  831. {
  832. if (prop.IsKnownType ||
  833. ClientConvert.IsKnownType(MaterializeAtom.GetEntryClientType(property.TypeName, context, prop.PropertyType, true).ElementType))
  834. {
  835. throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
  836. }
  837. bool needToSet = false;
  838. ClientType complexType = ClientType.Create(prop.PropertyType);
  839. object complexInstance = prop.GetValue(instance);
  840. if (complexInstance == null)
  841. {
  842. complexInstance = complexType.CreateInstance();
  843. needToSet = true;
  844. }
  845. MaterializeDataValues(complexType, property.Properties, ignoreMissingProperties, context);
  846. ApplyDataValues(complexType, property.Properties, ignoreMissingProperties, context, complexInstance);
  847. if (needToSet)
  848. {
  849. prop.SetValue(instance, complexInstance, property.Name, true );
  850. }
  851. }
  852. else
  853. {
  854. prop.SetValue(instance, property.MaterializedValue, property.Name, true );
  855. }
  856. }
  857. private static void ApplyDataValues(ClientType type, IEnumerable<AtomContentProperty> properties, bool ignoreMissingProperties, DataServiceContext context, object instance)
  858. {
  859. Debug.Assert(type != null, "type != null");
  860. Debug.Assert(properties != null, "properties != null");
  861. Debug.Assert(context != null, "properties != context");
  862. Debug.Assert(instance != null, "instance != context");
  863. foreach (var p in properties)
  864. {
  865. ApplyDataValue(type, p, ignoreMissingProperties, context, instance);
  866. }
  867. }
  868. private static void SetValueOnPath(List<AtomContentProperty> values, string path, string value, string typeName)
  869. {
  870. Debug.Assert(values != null, "values != null");
  871. Debug.Assert(path != null, "path != null");
  872. bool existing = true;
  873. AtomContentProperty property = null;
  874. foreach (string step in path.Split('/'))
  875. {
  876. if (values == null)
  877. {
  878. Debug.Assert(property != null, "property != null -- if values is null then this isn't the first step");
  879. property.EnsureProperties();
  880. values = property.Properties;
  881. }
  882. property = values.Where(v => v.Name == step).FirstOrDefault();
  883. if (property == null)
  884. {
  885. AtomContentProperty newProperty = new AtomContentProperty();
  886. existing = false;
  887. newProperty.Name = step;
  888. values.Add(newProperty);
  889. property = newProperty;
  890. }
  891. else
  892. {
  893. if (property.IsNull)
  894. {
  895. return;
  896. }
  897. }
  898. values = property.Properties;
  899. }
  900. Debug.Assert(property != null, "property != null -- property path should have at least one segment");
  901. if (existing == false)
  902. {
  903. property.TypeName = typeName;
  904. property.Text = value;
  905. }
  906. }
  907. private static void ApplyEntityPropertyMappings(AtomEntry entry, ClientType entryType)
  908. {
  909. Debug.Assert(entry != null, "entry != null");
  910. Debug.Assert(entry.Tag is XElement, "entry.Tag is XElement");
  911. Debug.Assert(entryType != null, "entryType != null -- othewise how would we know to apply property mappings (note that for projections entry.ActualType may be different that entryType)?");
  912. Debug.Assert(!entry.EntityPropertyMappingsApplied, "!entry.EntityPropertyMappingsApplied -- EPM should happen only once per entry");
  913. if (entryType.HasEntityPropertyMappings)
  914. {
  915. XElement entryElement = entry.Tag as XElement;
  916. Debug.Assert(entryElement != null, "entryElement != null");
  917. ApplyEntityPropertyMappings(entry, entryElement, entryType.EpmTargetTree.SyndicationRoot);
  918. ApplyEntityPropertyMappings(entry, entryElement, entryType.EpmTargetTree.NonSyndicationRoot);
  919. }
  920. entry.EntityPropertyMappingsApplied = true;
  921. }
  922. private static void ApplyEntityPropertyMappings(AtomEntry entry, XElement entryElement, EpmTargetPathSegment target)
  923. {
  924. Debug.Assert(target != null, "target != null");
  925. Debug.Assert(!target.HasContent, "!target.HasContent");
  926. Stack<System.Data.Services.Common.EpmTargetPathSegment> segments = new Stack<System.Data.Services.Common.EpmTargetPathSegment>();
  927. Stack<XElement> elements = new Stack<XElement>();
  928. segments.Push(target);
  929. elements.Push(entryElement);
  930. while (segments.Count > 0)
  931. {
  932. System.Data.Services.Common.EpmTargetPathSegment segment = segments.Pop();
  933. XElement element = elements.Pop();
  934. if (segment.HasContent)
  935. {
  936. var node = element.Nodes().Where(n => n.NodeType == XmlNodeType.Text || n.NodeType == XmlNodeType.SignificantWhitespace).FirstOrDefault();
  937. string elementValue = (node == null) ? null : ((XText)node).Value;
  938. Debug.Assert(segment.EpmInfo != null, "segment.EpmInfo != null -- otherwise segment.HasValue should be false");
  939. string path = segment.EpmInfo.Attribute.SourcePath;
  940. string typeName = (string)element.Attribute(XName.Get(XmlConstants.AtomTypeAttributeName, XmlConstants.DataWebMetadataNamespace));
  941. SetValueOnPath(entry.DataValues, path, elementValue, typeName);
  942. }
  943. foreach (var item in segment.SubSegments)
  944. {
  945. if (item.IsAttribute)
  946. {
  947. string localName = item.SegmentName.Substring(1);
  948. var attribute = element.Attribute(XName.Get(localName, item.SegmentNamespaceUri));
  949. if (attribute != null)
  950. {
  951. SetValueOnPath(entry.DataValues, item.EpmInfo.Attribute.SourcePath, attribute.Value, null);
  952. }
  953. }
  954. else
  955. {
  956. var child = element.Element(XName.Get(item.SegmentName, item.SegmentNamespaceUri));
  957. if (child != null)
  958. {
  959. segments.Push(item);
  960. elements.Push(child);
  961. }
  962. }
  963. }
  964. Debug.Assert(segments.Count == elements.Count, "segments.Count == elements.Count -- otherwise they're out of sync");
  965. }
  966. }
  967. private static AtomContentProperty GetPropertyOrThrow(List<AtomContentProperty> properties, string propertyName)
  968. {
  969. AtomContentProperty atomProperty = null;
  970. if (properties != null)
  971. {
  972. atomProperty = properties.Where(p => p.Name == propertyName).FirstOrDefault();
  973. }
  974. if (atomProperty == null)
  975. {
  976. throw new InvalidOperationException(Strings.AtomMaterializer_PropertyMissing(propertyName));
  977. }
  978. Debug.Assert(atomProperty != null, "atomProperty != null");
  979. return atomProperty;
  980. }
  981. private static AtomContentProperty GetPropertyOrThrow(AtomEntry entry, string propertyName)
  982. {
  983. AtomContentProperty atomProperty = null;
  984. var properties = entry.DataValues;
  985. if (properties != null)
  986. {
  987. atomProperty = properties.Where(p => p.Name == propertyName).FirstOrDefault();
  988. }
  989. if (atomProperty == null)
  990. {
  991. throw new InvalidOperationException(Strings.AtomMaterializer_PropertyMissingFromEntry(propertyName, entry.Identity));
  992. }
  993. Debug.Assert(atomProperty != null, "atomProperty != null");
  994. return atomProperty;
  995. }
  996. private void MergeLists(AtomEntry entry, ClientType.ClientProperty property, IEnumerable list, Uri nextLink, ProjectionPlan plan)
  997. {
  998. Debug.Assert(entry != null, "entry != null");
  999. Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null");
  1000. Debug.Assert(property != null, "property != null");
  1001. Debug.Assert(entry != null, "entry != null");
  1002. Debug.Assert(plan != null || nextLink == null, "plan != null || nextLink == null");
  1003. if (entry.ShouldUpdateFromPayload &&
  1004. property.NullablePropertyType == list.GetType() &&
  1005. property.GetValue(entry.ResolvedObject) == null)
  1006. {
  1007. property.SetValue(entry.ResolvedObject, list, property.PropertyName, false );
  1008. this.FoundNextLinkForCollection(list, nextLink, plan);
  1009. foreach (object item in list)
  1010. {
  1011. this.log.AddedLink(entry, property.PropertyName, item);
  1012. }
  1013. return;
  1014. }
  1015. this.ApplyItemsToCollection(entry, property, list, nextLink, plan);
  1016. }
  1017. private bool TryResolveAsTarget(AtomEntry entry)
  1018. {
  1019. if (entry.ResolvedObject == null)
  1020. {
  1021. return false;
  1022. }
  1023. Debug.Assert(
  1024. entry.ResolvedObject == this.TargetInstance,
  1025. "entry.ResolvedObject == this.TargetInstance -- otherwise there we ResolveOrCreateInstance more than once on the same entry");
  1026. Debug.Assert(
  1027. this.mergeOption == MergeOption.OverwriteChanges || this.mergeOption == MergeOption.PreserveChanges,
  1028. "MergeOption.OverwriteChanges and MergeOption.PreserveChanges are the only expected values during SaveChanges");
  1029. entry.ActualType = ClientType.Create(entry.ResolvedObject.GetType());
  1030. this.log.FoundTargetInstance(entry);
  1031. entry.ShouldUpdateFromPayload = this.mergeOption == MergeOption.PreserveChanges ? false : true;
  1032. entry.EntityHasBeenResolved = true;
  1033. return true;
  1034. }
  1035. private bool TryResolveFromContext(AtomEntry entry, Type expectedEntryType)
  1036. {
  1037. bool tracking = this.mergeOption != MergeOption.NoTracking;
  1038. if (tracking)
  1039. {
  1040. EntityStates state;
  1041. entry.ResolvedObject = this.context.TryGetEntity(entry.Identity, entry.ETagText, this.mergeOption, out state);
  1042. if (entry.ResolvedObject != null)
  1043. {
  1044. if (!expectedEntryType.IsInstanceOfType(entry.ResolvedObject))
  1045. {
  1046. throw Error.InvalidOperation(Strings.Deserialize_Current(expectedEntryType, entry.ResolvedObject.GetType()));
  1047. }
  1048. entry.ActualType = ClientType.Create(entry.ResolvedObject.GetType());
  1049. entry.EntityHasBeenResolved = true;
  1050. entry.ShouldUpdateFromPayload =
  1051. this.mergeOption == MergeOption.OverwriteChanges ||
  1052. (this.mergeOption == MergeOption.PreserveChanges && state == EntityStates.Unchanged) ||
  1053. (this.mergeOption == MergeOption.PreserveChanges && state == EntityStates.Deleted);
  1054. this.log.FoundExistingInstance(entry);
  1055. return true;
  1056. }
  1057. }
  1058. return false;
  1059. }
  1060. private void ResolveByCreatingWithType(AtomEntry entry, Type type)
  1061. {
  1062. Debug.Assert(
  1063. entry.ResolvedObject == null,
  1064. "entry.ResolvedObject == null -- otherwise we're about to overwrite - should never be called");
  1065. entry.ActualType = ClientType.Create(type);
  1066. entry.ResolvedObject = Activator.CreateInstance(type);
  1067. entry.CreatedByMaterializer = true;
  1068. entry.ShouldUpdateFromPayload = true;
  1069. entry.EntityHasBeenResolved = true;
  1070. this.log.CreatedInstance(entry);
  1071. }
  1072. private void ResolveByCreating(AtomEntry entry, Type expectedEntryType)
  1073. {
  1074. Debug.Assert(
  1075. entry.ResolvedObject == null,
  1076. "entry.ResolvedObject == null -- otherwise we're about to overwrite - should never be called");
  1077. ClientType actualType = MaterializeAtom.GetEntryClientType(entry.TypeName, this.context, expectedEntryType, true);
  1078. Debug.Assert(actualType != null, "actualType != null -- otherwise ClientType.Create returned a null value");
  1079. this.ResolveByCreatingWithType(entry, actualType.ElementType);
  1080. }
  1081. private bool TryResolveAsCreated(AtomEntry entry)
  1082. {
  1083. AtomEntry existingEntry;
  1084. if (!this.log.TryResolve(entry, out existingEntry))
  1085. {
  1086. return false;
  1087. }
  1088. Debug.Assert(
  1089. existingEntry.ResolvedObject != null,
  1090. "existingEntry.ResolvedObject != null -- how did it get there otherwise?");
  1091. entry.ActualType = existingEntry.ActualType;
  1092. entry.ResolvedObject = existingEntry.ResolvedObject;
  1093. entry.CreatedByMaterializer = existingEntry.CreatedByMaterializer;
  1094. entry.ShouldUpdateFromPayload = existingEntry.ShouldUpdateFromPayload;
  1095. entry.EntityHasBeenResolved = true;
  1096. return true;
  1097. }
  1098. private void ResolveOrCreateInstance(AtomEntry entry, Type expectedEntryType)
  1099. {
  1100. Debug.Assert(entry != null, "entry != null");
  1101. Debug.Assert(expectedEntryType != null, "expectedEntryType != null");
  1102. Debug.Assert(entry.EntityHasBeenResolved == false, "entry.EntityHasBeenResolved == false");
  1103. if (!this.TryResolveAsTarget(entry))
  1104. {
  1105. if (entry.Identity == null)
  1106. {
  1107. throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement);
  1108. }
  1109. if (!this.TryResolveAsCreated(entry))
  1110. {
  1111. if (!this.TryResolveFromContext(entry, expectedEntryType))
  1112. {
  1113. this.ResolveByCreating(entry, expectedEntryType);
  1114. }
  1115. }
  1116. }
  1117. Debug.Assert(entry.ActualType != null, "entry.ActualType != null");
  1118. Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null");
  1119. Debug.Assert(entry.EntityHasBeenResolved, "entry.EntityHasBeenResolved");
  1120. return;
  1121. }
  1122. private void ApplyFeedToCollection(
  1123. AtomEntry entry,
  1124. ClientType.ClientProperty property,
  1125. AtomFeed feed,
  1126. bool includeLinks)
  1127. {
  1128. Debug.Assert(entry != null, "entry != null");
  1129. Debug.Assert(property != null, "property != null");
  1130. Debug.Assert(feed != null, "feed != null");
  1131. ClientType collectionType = ClientType.Create(property.CollectionType);
  1132. foreach (AtomEntry feedEntry in feed.Entries)
  1133. {
  1134. this.Materialize(feedEntry, collectionType.ElementType, includeLinks);
  1135. }
  1136. ProjectionPlan continuationPlan = includeLinks ? CreatePlanForDirectMaterialization(property.CollectionType) : CreatePlanForShallowMaterialization(property.CollectionType);
  1137. this.ApplyItemsToCollection(entry, property, feed.Entries.Select(e => e.ResolvedObject), feed.NextLink, continuationPlan);
  1138. }
  1139. private void ApplyItemsToCollection(
  1140. AtomEntry entry,
  1141. ClientType.ClientProperty property,
  1142. IEnumerable items,
  1143. Uri nextLink,
  1144. ProjectionPlan continuationPlan)
  1145. {
  1146. Debug.Assert(entry != null, "entry != null");
  1147. Debug.Assert(property != null, "property != null");
  1148. Debug.Assert(items != null, "items != null");
  1149. object collection = entry.ShouldUpdateFromPayload ? GetOrCreateCollectionProperty(entry.ResolvedObject, property, null) : null;
  1150. ClientType collectionType = ClientType.Create(property.CollectionType);
  1151. foreach (object item in items)
  1152. {
  1153. if (!collectionType.ElementType.IsAssignableFrom(item.GetType()))
  1154. {
  1155. string message = Strings.AtomMaterializer_EntryIntoCollectionMismatch(
  1156. item.GetType().FullName,
  1157. collectionType.ElementType.FullName);
  1158. throw new InvalidOperationException(message);
  1159. }
  1160. if (entry.ShouldUpdateFromPayload)
  1161. {
  1162. property.SetValue(collection, item, property.PropertyName, true );
  1163. this.log.AddedLink(entry, property.PropertyName, item);
  1164. }
  1165. }
  1166. if (entry.ShouldUpdateFromPayload)
  1167. {
  1168. this.FoundNextLinkForCollection(collection as IEnumerable, nextLink, continuationPlan);
  1169. }
  1170. else
  1171. {
  1172. this.FoundNextLinkForUnmodifiedCollection(property.GetValue(entry.ResolvedObject) as IEnumerable);
  1173. }
  1174. if (this.mergeOption == MergeOption.OverwriteChanges || this.mergeOption == MergeOption.PreserveChanges)
  1175. {
  1176. var itemsToRemove =
  1177. from x in this.context.GetLinks(entry.ResolvedObject, property.PropertyName)
  1178. where MergeOption.OverwriteChanges == this.mergeOption || EntityStates.Added != x.State
  1179. select x.Target;
  1180. itemsToRemove = itemsToRemove.Except(EnumerateAsElementType<object>(items));
  1181. foreach (var item in itemsToRemove)
  1182. {
  1183. if (collection != null)
  1184. {
  1185. property.RemoveValue(collection, item);
  1186. }
  1187. this.log.RemovedLink(entry, property.PropertyName, item);
  1188. }
  1189. }
  1190. }
  1191. private void FoundNextLinkForCollection(IEnumerable collection, Uri link, ProjectionPlan plan)
  1192. {
  1193. Debug.Assert(plan != null || link == null, "plan != null || link == null");
  1194. if (collection != null && !this.nextLinkTable.ContainsKey(collection))
  1195. {
  1196. DataServiceQueryContinuation continuation = DataServiceQueryContinuation.Create(link, plan);
  1197. this.nextLinkTable.Add(collection, continuation);
  1198. Util.SetNextLinkForCollection(collection, continuation);
  1199. }
  1200. }
  1201. private void FoundNextLinkForUnmodifiedCollection(IEnumerable collection)
  1202. {
  1203. if (collection != null && !this.nextLinkTable.ContainsKey(collection))
  1204. {
  1205. this.nextLinkTable.Add(collection, null);
  1206. }
  1207. }
  1208. private void Materialize(AtomEntry entry, Type expectedEntryType, bool includeLinks)
  1209. {
  1210. Debug.Assert(entry != null, "entry != null");
  1211. Debug.Assert(entry.DataValues != null, "entry.DataValues != null -- otherwise not correctly initialized");
  1212. Debug.Assert(
  1213. entry.ResolvedObject == null || entry.ResolvedObject == this.targetInstance,
  1214. "entry.ResolvedObject == null || entry.ResolvedObject == this.targetInstance -- otherwise getting called twice");
  1215. Debug.Assert(expectedEntryType != null, "expectedType != null");
  1216. this.ResolveOrCreateInstance(entry, expectedEntryType);
  1217. Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise ResolveOrCreateInstnace didn't do its job");
  1218. this.MaterializeResolvedEntry(entry, includeLinks);
  1219. }
  1220. private void MaterializeResolvedEntry(AtomEntry entry, bool includeLinks)
  1221. {
  1222. Debug.Assert(entry != null, "entry != null");
  1223. Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!");
  1224. ClientType actualType = entry.ActualType;
  1225. if (!entry.EntityPropertyMappingsApplied)
  1226. {
  1227. ApplyEntityPropertyMappings(entry, entry.ActualType);
  1228. }
  1229. MaterializeDataValues(actualType, entry.DataValues, this.ignoreMissingProperties, this.context);
  1230. foreach (var e in entry.DataValues)
  1231. {
  1232. var prop = actualType.GetProperty(e.Name, this.ignoreMissingProperties);
  1233. if (prop == null)
  1234. {
  1235. continue;
  1236. }
  1237. if (entry.ShouldUpdateFromPayload == false && e.Entry == null && e.Feed == null)
  1238. {
  1239. continue;
  1240. }
  1241. if (!includeLinks && (e.Entry != null || e.Feed != null))
  1242. {
  1243. continue;
  1244. }
  1245. ValidatePropertyMatch(prop, e);
  1246. AtomFeed feedValue = e.Feed;
  1247. if (feedValue != null)
  1248. {
  1249. Debug.Assert(includeLinks, "includeLinks -- otherwise we shouldn't be materializing this entry");
  1250. this.ApplyFeedToCollection(entry, prop, feedValue, includeLinks);
  1251. }
  1252. else if (e.Entry != null)
  1253. {
  1254. if (!e.IsNull)
  1255. {
  1256. Debug.Assert(includeLinks, "includeLinks -- otherwise we shouldn't be materializing this entry");
  1257. this.Materialize(e.Entry, prop.PropertyType, includeLinks);
  1258. }
  1259. if (entry.ShouldUpdateFromPayload)
  1260. {
  1261. prop.SetValue(entry.ResolvedObject, e.Entry.ResolvedObject, e.Name, true );
  1262. this.log.SetLink(entry, prop.PropertyName, e.Entry.ResolvedObject);
  1263. }
  1264. }
  1265. else
  1266. {
  1267. Debug.Assert(entry.ShouldUpdateFromPayload, "entry.ShouldUpdateFromPayload -- otherwise we're about to set a property we shouldn't");
  1268. ApplyDataValue(actualType, e, this.ignoreMissingProperties, this.context, entry.ResolvedObject);
  1269. }
  1270. }
  1271. Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise we didn't do any useful work");
  1272. if (this.materializedObjectCallback != null)
  1273. {
  1274. this.materializedObjectCallback(entry.Tag, entry.ResolvedObject);
  1275. }
  1276. }
  1277. #endregion Private methods.
  1278. }
  1279. }