PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/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

Large files files are truncated, but you can click here to view the full file

  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 = proper

Large files files are truncated, but you can click here to view the full file