/mcs/class/System.Data.Services.Client/Client/System/Data/Services/Client/AtomMaterializer.cs
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
- //Copyright 2010 Microsoft Corporation
- //
- //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
- //You may obtain a copy of the License at
- //
- //http://www.apache.org/licenses/LICENSE-2.0
- //
- //Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- //See the License for the specific language governing permissions and limitations under the License.
-
-
- namespace System.Data.Services.Client
- {
- #region Namespaces.
-
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Data.Services.Common;
- using System.Diagnostics;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Xml;
- using System.Xml.Linq;
-
- #endregion Namespaces.
-
- internal static class AtomMaterializerInvoker
- {
- internal static IEnumerable<T> EnumerateAsElementType<T>(IEnumerable source)
- {
- return AtomMaterializer.EnumerateAsElementType<T>(source);
- }
-
- internal static List<TTarget> ListAsElementType<T, TTarget>(object materializer, IEnumerable<T> source) where T : TTarget
- {
- Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
- return AtomMaterializer.ListAsElementType<T, TTarget>((AtomMaterializer)materializer, source);
- }
-
- internal static bool ProjectionCheckValueForPathIsNull(
- object entry,
- Type expectedType,
- object path)
- {
- Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
- Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
- return AtomMaterializer.ProjectionCheckValueForPathIsNull((AtomEntry)entry, expectedType, (ProjectionPath)path);
- }
-
- internal static IEnumerable ProjectionSelect(
- object materializer,
- object entry,
- Type expectedType,
- Type resultType,
- object path,
- Func<object, object, Type, object> selector)
- {
- Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
- Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
- Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
- return AtomMaterializer.ProjectionSelect((AtomMaterializer)materializer, (AtomEntry)entry, expectedType, resultType, (ProjectionPath)path, selector);
- }
-
- internal static AtomEntry ProjectionGetEntry(object entry, string name)
- {
- Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
- return AtomMaterializer.ProjectionGetEntry((AtomEntry)entry, name);
- }
-
- internal static object ProjectionInitializeEntity(
- object materializer,
- object entry,
- Type expectedType,
- Type resultType,
- string[] properties,
- Func<object, object, Type, object>[] propertyValues)
- {
- Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
- Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
- return AtomMaterializer.ProjectionInitializeEntity((AtomMaterializer)materializer, (AtomEntry)entry, expectedType, resultType, properties, propertyValues);
- }
-
- internal static object ProjectionValueForPath(object materializer, object entry, Type expectedType, object path)
- {
- Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
- Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
- Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
- return AtomMaterializer.ProjectionValueForPath((AtomMaterializer)materializer, (AtomEntry)entry, expectedType, (ProjectionPath)path);
- }
-
- internal static object DirectMaterializePlan(object materializer, object entry, Type expectedEntryType)
- {
- Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
- Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
- return AtomMaterializer.DirectMaterializePlan((AtomMaterializer)materializer, (AtomEntry)entry, expectedEntryType);
- }
-
- internal static object ShallowMaterializePlan(object materializer, object entry, Type expectedEntryType)
- {
- Debug.Assert(materializer.GetType() == typeof(AtomMaterializer), "materializer.GetType() == typeof(AtomMaterializer)");
- Debug.Assert(entry.GetType() == typeof(AtomEntry), "entry.GetType() == typeof(AtomEntry)");
- return AtomMaterializer.ShallowMaterializePlan((AtomMaterializer)materializer, (AtomEntry)entry, expectedEntryType);
- }
- }
-
- [DebuggerDisplay("AtomMaterializer {parser}")]
- internal class AtomMaterializer
- {
- #region Private fields.
-
- private readonly DataServiceContext context;
-
- private readonly Type expectedType;
-
-
- private readonly AtomMaterializerLog log;
-
- private readonly ProjectionPlan materializeEntryPlan;
-
- private readonly Action<object, object> materializedObjectCallback;
-
- private readonly MergeOption mergeOption;
-
- private readonly Dictionary<IEnumerable, DataServiceQueryContinuation> nextLinkTable;
-
- private readonly AtomParser parser;
-
- private object currentValue;
-
- private bool ignoreMissingProperties;
-
- private object targetInstance;
-
- #endregion Private fields.
-
- #region Constructors.
-
- internal AtomMaterializer(
- AtomParser parser,
- DataServiceContext context,
- Type expectedType,
- bool ignoreMissingProperties,
- MergeOption mergeOption,
- AtomMaterializerLog log,
- Action<object, object> materializedObjectCallback,
- QueryComponents queryComponents,
- ProjectionPlan plan)
- {
- Debug.Assert(context != null, "context != null");
- Debug.Assert(parser != null, "parser != null");
- Debug.Assert(log != null, "log != null");
-
- this.context = context;
- this.parser = parser;
- this.expectedType = expectedType;
- this.ignoreMissingProperties = ignoreMissingProperties;
- this.mergeOption = mergeOption;
- this.log = log;
- this.materializedObjectCallback = materializedObjectCallback;
- this.nextLinkTable = new Dictionary<IEnumerable, DataServiceQueryContinuation>(ReferenceEqualityComparer<IEnumerable>.Instance);
- this.materializeEntryPlan = plan ?? CreatePlan(queryComponents);
- }
-
- #endregion Constructors.
-
- #region Internal properties.
-
- internal DataServiceContext Context
- {
- get { return this.context; }
- }
-
- internal ProjectionPlan MaterializeEntryPlan
- {
- get { return this.materializeEntryPlan; }
- }
-
- internal object TargetInstance
- {
- get
- {
- return this.targetInstance;
- }
-
- set
- {
- Debug.Assert(value != null, "value != null -- otherwise we have no instance target.");
- this.targetInstance = value;
- }
- }
-
- internal AtomFeed CurrentFeed
- {
- get
- {
- return this.parser.CurrentFeed;
- }
- }
-
- internal AtomEntry CurrentEntry
- {
- get
- {
- return this.parser.CurrentEntry;
- }
- }
-
- internal object CurrentValue
- {
- get
- {
- return this.currentValue;
- }
- }
-
- internal AtomMaterializerLog Log
- {
- get { return this.log; }
- }
-
- internal Dictionary<IEnumerable, DataServiceQueryContinuation> NextLinkTable
- {
- get { return this.nextLinkTable; }
- }
-
- internal bool IsEndOfStream
- {
- get { return this.parser.DataKind == AtomDataKind.Finished; }
- }
-
- #endregion Internal properties.
-
- #region Projection support.
-
- internal static IEnumerable<T> EnumerateAsElementType<T>(IEnumerable source)
- {
- Debug.Assert(source != null, "source != null");
-
- IEnumerable<T> typedSource = source as IEnumerable<T>;
- if (typedSource != null)
- {
- return typedSource;
- }
- else
- {
- return EnumerateAsElementTypeInternal<T>(source);
- }
- }
-
- internal static IEnumerable<T> EnumerateAsElementTypeInternal<T>(IEnumerable source)
- {
- Debug.Assert(source != null, "source != null");
-
- foreach (object item in source)
- {
- yield return (T)item;
- }
- }
-
- internal static bool IsDataServiceCollection(Type type)
- {
- while (type != null)
- {
- if (type.IsGenericType && WebUtil.IsDataServiceCollectionType(type.GetGenericTypeDefinition()))
- {
- return true;
- }
-
- type = type.BaseType;
- }
-
- return false;
- }
-
- internal static List<TTarget> ListAsElementType<T, TTarget>(AtomMaterializer materializer, IEnumerable<T> source) where T : TTarget
- {
- Debug.Assert(materializer != null, "materializer != null");
- Debug.Assert(source != null, "source != null");
-
- List<TTarget> typedSource = source as List<TTarget>;
- if (typedSource != null)
- {
- return typedSource;
- }
-
- List<TTarget> list;
- IList sourceList = source as IList;
- if (sourceList != null)
- {
- list = new List<TTarget>(sourceList.Count);
- }
- else
- {
- list = new List<TTarget>();
- }
-
- foreach (T item in source)
- {
- list.Add((TTarget)item);
- }
-
- DataServiceQueryContinuation continuation;
- if (materializer.nextLinkTable.TryGetValue(source, out continuation))
- {
- materializer.nextLinkTable[list] = continuation;
- }
-
- return list;
- }
-
- internal static bool ProjectionCheckValueForPathIsNull(
- AtomEntry entry,
- Type expectedType,
- ProjectionPath path)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(path != null, "path != null");
-
- if (path.Count == 0 || path.Count == 1 && path[0].Member == null)
- {
- return entry.IsNull;
- }
-
- bool result = false;
- AtomContentProperty atomProperty = null;
- List<AtomContentProperty> properties = entry.DataValues;
- for (int i = 0; i < path.Count; i++)
- {
- var segment = path[i];
- if (segment.Member == null)
- {
- continue;
- }
-
- bool segmentIsLeaf = i == path.Count - 1;
- string propertyName = segment.Member;
- ClientType.ClientProperty property = ClientType.Create(expectedType).GetProperty(propertyName, false);
- atomProperty = GetPropertyOrThrow(properties, propertyName);
- ValidatePropertyMatch(property, atomProperty);
- if (atomProperty.Feed != null)
- {
- Debug.Assert(segmentIsLeaf, "segmentIsLeaf -- otherwise the path generated traverses a feed, which should be disallowed");
- result = false;
- }
- else
- {
- Debug.Assert(
- atomProperty.Entry != null,
- "atomProperty.Entry != null -- otherwise a primitive property / complex type is being rewritte with a null check; this is only supported for entities and collection");
- if (segmentIsLeaf)
- {
- result = atomProperty.Entry.IsNull;
- }
-
- properties = atomProperty.Entry.DataValues;
- entry = atomProperty.Entry;
- }
-
- expectedType = property.PropertyType;
- }
-
- return result;
- }
-
- internal static IEnumerable ProjectionSelect(
- AtomMaterializer materializer,
- AtomEntry entry,
- Type expectedType,
- Type resultType,
- ProjectionPath path,
- Func<object, object, Type, object> selector)
- {
- ClientType entryType = entry.ActualType ?? ClientType.Create(expectedType);
- IEnumerable list = (IEnumerable)Util.ActivatorCreateInstance(typeof(List<>).MakeGenericType(resultType));
- AtomContentProperty atomProperty = null;
- ClientType.ClientProperty property = null;
- for (int i = 0; i < path.Count; i++)
- {
- var segment = path[i];
- if (segment.Member == null)
- {
- continue;
- }
-
- string propertyName = segment.Member;
- property = entryType.GetProperty(propertyName, false);
- atomProperty = GetPropertyOrThrow(entry, propertyName);
-
- if (atomProperty.Entry != null)
- {
- entry = atomProperty.Entry;
- entryType = ClientType.Create(property.NullablePropertyType, false);
- }
- }
-
- ValidatePropertyMatch(property, atomProperty);
- AtomFeed sourceFeed = atomProperty.Feed;
- Debug.Assert(
- sourceFeed != null,
- "sourceFeed != null -- otherwise ValidatePropertyMatch should have thrown or property isn't a collection (and should be part of this plan)");
-
- Action<object, object> addMethod = GetAddToCollectionDelegate(list.GetType());
- foreach (var paramEntry in sourceFeed.Entries)
- {
- object projected = selector(materializer, paramEntry, property.CollectionType );
- addMethod(list, projected);
- }
-
- ProjectionPlan plan = new ProjectionPlan();
- plan.LastSegmentType = property.CollectionType;
- plan.Plan = selector;
- plan.ProjectedType = resultType;
-
- materializer.FoundNextLinkForCollection(list, sourceFeed.NextLink, plan);
-
- return list;
- }
-
- internal static AtomEntry ProjectionGetEntry(AtomEntry entry, string name)
- {
- Debug.Assert(entry != null, "entry != null -- ProjectionGetEntry never returns a null entry, and top-level materialization shouldn't pass one in");
-
- AtomContentProperty property = GetPropertyOrThrow(entry, name);
- if (property.Entry == null)
- {
- throw new InvalidOperationException(Strings.AtomMaterializer_PropertyNotExpectedEntry(name, entry.Identity));
- }
-
- CheckEntryToAccessNotNull(property.Entry, name);
-
- return property.Entry;
- }
-
- internal static object ProjectionInitializeEntity(
- AtomMaterializer materializer,
- AtomEntry entry,
- Type expectedType,
- Type resultType,
- string[] properties,
- Func<object, object, Type, object>[] propertyValues)
- {
- if (entry == null || entry.IsNull)
- {
- throw new NullReferenceException(Strings.AtomMaterializer_EntryToInitializeIsNull(resultType.FullName));
- }
-
- object result;
- if (!entry.EntityHasBeenResolved)
- {
- AtomMaterializer.ProjectionEnsureEntryAvailableOfType(materializer, entry, resultType);
- }
- else if (!resultType.IsAssignableFrom(entry.ActualType.ElementType))
- {
- string message = Strings.AtomMaterializer_ProjectEntityTypeMismatch(
- resultType.FullName,
- entry.ActualType.ElementType.FullName,
- entry.Identity);
- throw new InvalidOperationException(message);
- }
-
- result = entry.ResolvedObject;
-
- for (int i = 0; i < properties.Length; i++)
- {
- var property = entry.ActualType.GetProperty(properties[i], materializer.ignoreMissingProperties);
- object value = propertyValues[i](materializer, entry, expectedType);
- if (entry.ShouldUpdateFromPayload && ClientType.Create(property.NullablePropertyType, false).IsEntityType)
- {
- materializer.Log.SetLink(entry, property.PropertyName, value);
- }
-
- bool isEntity = property.CollectionType == null || !ClientType.CheckElementTypeIsEntity(property.CollectionType);
- if (entry.ShouldUpdateFromPayload)
- {
- if (isEntity)
- {
- property.SetValue(result, value, property.PropertyName, false);
- }
- else
- {
- IEnumerable valueAsEnumerable = (IEnumerable)value;
- DataServiceQueryContinuation continuation = materializer.nextLinkTable[valueAsEnumerable];
- Uri nextLinkUri = continuation == null ? null : continuation.NextLinkUri;
- ProjectionPlan plan = continuation == null ? null : continuation.Plan;
- materializer.MergeLists(entry, property, valueAsEnumerable, nextLinkUri, plan);
- }
- }
- else if (!isEntity)
- {
- materializer.FoundNextLinkForUnmodifiedCollection(property.GetValue(entry.ResolvedObject) as IEnumerable);
- }
- }
-
- return result;
- }
-
- internal static object ProjectionValueForPath(AtomMaterializer materializer, AtomEntry entry, Type expectedType, ProjectionPath path)
- {
- Debug.Assert(materializer != null, "materializer != null");
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(path != null, "path != null");
-
- if (path.Count == 0 || path.Count == 1 && path[0].Member == null)
- {
- if (!entry.EntityHasBeenResolved)
- {
- materializer.Materialize(entry, expectedType, false);
- }
-
- return entry.ResolvedObject;
- }
-
- object result = null;
- AtomContentProperty atomProperty = null;
- List<AtomContentProperty> properties = entry.DataValues;
- for (int i = 0; i < path.Count; i++)
- {
- var segment = path[i];
- if (segment.Member == null)
- {
- continue;
- }
-
- bool segmentIsLeaf = i == path.Count - 1;
- string propertyName = segment.Member;
-
- if (segmentIsLeaf)
- {
- CheckEntryToAccessNotNull(entry, propertyName);
- if (!entry.EntityPropertyMappingsApplied)
- {
- ClientType attributeSourceType = MaterializeAtom.GetEntryClientType(entry.TypeName, materializer.context, expectedType, false);
- ApplyEntityPropertyMappings(entry, attributeSourceType);
- }
- }
-
- ClientType.ClientProperty property = ClientType.Create(expectedType).GetProperty(propertyName, false);
- atomProperty = GetPropertyOrThrow(properties, propertyName);
-
- ValidatePropertyMatch(property, atomProperty);
-
- AtomFeed feedValue = atomProperty.Feed;
- if (feedValue != null)
- {
- Debug.Assert(segmentIsLeaf, "segmentIsLeaf -- otherwise the path generated traverses a feed, which should be disallowed");
-
- Type collectionType = ClientType.GetImplementationType(segment.ProjectionType, typeof(ICollection<>));
- if (collectionType == null)
- {
- collectionType = ClientType.GetImplementationType(segment.ProjectionType, typeof(IEnumerable<>));
- }
-
- Debug.Assert(
- collectionType != null,
- "collectionType != null -- otherwise the property should never have been recognized as a collection");
-
- Type nestedExpectedType = collectionType.GetGenericArguments()[0];
- Type feedType = segment.ProjectionType;
- if (feedType.IsInterface || IsDataServiceCollection(feedType))
- {
- feedType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(nestedExpectedType);
- }
-
- IEnumerable list = (IEnumerable)Util.ActivatorCreateInstance(feedType);
- MaterializeToList(materializer, list, nestedExpectedType, feedValue.Entries);
-
- if (IsDataServiceCollection(segment.ProjectionType))
- {
- Type dataServiceCollectionType = WebUtil.GetDataServiceCollectionOfT(nestedExpectedType);
- list = (IEnumerable)Util.ActivatorCreateInstance(
- dataServiceCollectionType,
- list,
- TrackingMode.None); }
-
- ProjectionPlan plan = CreatePlanForShallowMaterialization(nestedExpectedType);
- materializer.FoundNextLinkForCollection(list, feedValue.NextLink, plan);
- result = list;
- }
- else if (atomProperty.Entry != null)
- {
- if (segmentIsLeaf && !atomProperty.Entry.EntityHasBeenResolved && !atomProperty.IsNull)
- {
- materializer.Materialize(atomProperty.Entry, property.PropertyType, false);
- }
-
- properties = atomProperty.Entry.DataValues;
- result = atomProperty.Entry.ResolvedObject;
- entry = atomProperty.Entry;
- }
- else
- {
- if (atomProperty.Properties != null)
- {
- if (atomProperty.MaterializedValue == null && !atomProperty.IsNull)
- {
- ClientType complexType = ClientType.Create(property.PropertyType);
- object complexInstance = Util.ActivatorCreateInstance(property.PropertyType);
- MaterializeDataValues(complexType, atomProperty.Properties, materializer.ignoreMissingProperties, materializer.context);
- ApplyDataValues(complexType, atomProperty.Properties, materializer.ignoreMissingProperties, materializer.context, complexInstance);
- atomProperty.MaterializedValue = complexInstance;
- }
- }
- else
- {
- MaterializeDataValue(property.NullablePropertyType, atomProperty, materializer.context);
- }
-
- properties = atomProperty.Properties;
- result = atomProperty.MaterializedValue;
- }
-
- expectedType = property.PropertyType;
- }
-
- return result;
- }
-
- internal static void ProjectionEnsureEntryAvailableOfType(AtomMaterializer materializer, AtomEntry entry, Type requiredType)
- {
- Debug.Assert(materializer != null, "materializer != null");
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(
- materializer.targetInstance == null,
- "materializer.targetInstance == null -- projection shouldn't have a target instance set; that's only used for POST replies");
-
- if (entry.EntityHasBeenResolved)
- {
- if (!requiredType.IsAssignableFrom(entry.ResolvedObject.GetType()))
- {
- throw new InvalidOperationException(
- "Expecting type '" + requiredType + "' for '" + entry.Identity + "', but found " +
- "a previously created instance of type '" + entry.ResolvedObject.GetType());
- }
-
- return;
- }
-
- if (entry.Identity == null)
- {
- throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement);
- }
-
- if (!materializer.TryResolveAsCreated(entry) &&
- !materializer.TryResolveFromContext(entry, requiredType))
- {
- materializer.ResolveByCreatingWithType(entry, requiredType);
- }
- else
- {
- if (!requiredType.IsAssignableFrom(entry.ResolvedObject.GetType()))
- {
- throw Error.InvalidOperation(Strings.Deserialize_Current(requiredType, entry.ResolvedObject.GetType()));
- }
- }
- }
-
- internal static object DirectMaterializePlan(AtomMaterializer materializer, AtomEntry entry, Type expectedEntryType)
- {
- materializer.Materialize(entry, expectedEntryType, true);
- return entry.ResolvedObject;
- }
-
- internal static object ShallowMaterializePlan(AtomMaterializer materializer, AtomEntry entry, Type expectedEntryType)
- {
- materializer.Materialize(entry, expectedEntryType, false);
- return entry.ResolvedObject;
- }
-
- internal static void ValidatePropertyMatch(ClientType.ClientProperty property, AtomContentProperty atomProperty)
- {
- Debug.Assert(property != null, "property != null");
- Debug.Assert(atomProperty != null, "atomProperty != null");
-
- if (property.IsKnownType && (atomProperty.Feed != null || atomProperty.Entry != null))
- {
- throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkLocalSimple);
- }
-
- if (atomProperty.Feed != null && property.CollectionType == null)
- {
- throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(property.PropertyName));
- }
-
- if (atomProperty.Entry != null && property.CollectionType != null)
- {
- throw Error.InvalidOperation(Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(property.PropertyName));
- }
- }
-
- #endregion Projection support.
-
- #region Internal methods.
-
- internal bool Read()
- {
- this.currentValue = null;
-
- this.nextLinkTable.Clear();
- while (this.parser.Read())
- {
- Debug.Assert(
- this.parser.DataKind != AtomDataKind.None,
- "parser.DataKind != AtomDataKind.None -- otherwise parser.Read() didn't update its state");
- Debug.Assert(
- this.parser.DataKind != AtomDataKind.Finished,
- "parser.DataKind != AtomDataKind.Finished -- otherwise parser.Read() shouldn't have returned true");
-
- switch (this.parser.DataKind)
- {
- case AtomDataKind.Feed:
- case AtomDataKind.FeedCount:
- break;
- case AtomDataKind.Entry:
- Debug.Assert(
- this.parser.CurrentEntry != null,
- "parser.CurrentEntry != null -- otherwise parser.DataKind shouldn't be Entry");
- this.CurrentEntry.ResolvedObject = this.TargetInstance;
- this.currentValue = this.materializeEntryPlan.Run(this, this.CurrentEntry, this.expectedType);
- return true;
- case AtomDataKind.PagingLinks:
- break;
- default:
- Debug.Assert(
- this.parser.DataKind == AtomDataKind.Custom,
- "parser.DataKind == AtomDataKind.Custom -- otherwise AtomMaterializer.Read switch is missing a case");
-
- Type underlyingExpectedType = Nullable.GetUnderlyingType(this.expectedType) ?? this.expectedType;
- ClientType targetType = ClientType.Create(underlyingExpectedType);
- if (ClientConvert.IsKnownType(underlyingExpectedType))
- {
- string elementText = this.parser.ReadCustomElementString();
- if (elementText != null)
- {
- this.currentValue = ClientConvert.ChangeType(elementText, underlyingExpectedType);
- }
-
- return true;
- }
- else if (!targetType.IsEntityType && this.parser.IsDataWebElement)
- {
- AtomContentProperty property = this.parser.ReadCurrentPropertyValue();
- if (property == null || property.IsNull)
- {
- this.currentValue = null;
- }
- else
- {
- this.currentValue = targetType.CreateInstance();
- MaterializeDataValues(targetType, property.Properties, this.ignoreMissingProperties, this.context);
- ApplyDataValues(targetType, property.Properties, this.ignoreMissingProperties, this.context, this.currentValue);
- }
-
- return true;
- }
-
- break;
- }
- }
-
- Debug.Assert(this.parser.DataKind == AtomDataKind.Finished, "parser.DataKind == AtomDataKind.None");
- Debug.Assert(this.parser.CurrentEntry == null, "parser.Current == null");
- return false;
- }
-
- internal void PropagateContinuation<T>(IEnumerable<T> from, DataServiceCollection<T> to)
- {
- DataServiceQueryContinuation continuation;
- if (this.nextLinkTable.TryGetValue(from, out continuation))
- {
- this.nextLinkTable.Add(to, continuation);
- Util.SetNextLinkForCollection(to, continuation);
- }
- }
-
- #endregion Internal methods.
-
- #region Private methods.
-
- private static void CheckEntryToAccessNotNull(AtomEntry entry, string name)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(name != null, "name != null");
-
- if (entry.IsNull)
- {
- throw new NullReferenceException(Strings.AtomMaterializer_EntryToAccessIsNull(name));
- }
- }
-
- private static ProjectionPlan CreatePlan(QueryComponents queryComponents)
- {
- LambdaExpression projection = queryComponents.Projection;
- ProjectionPlan result;
- if (projection == null)
- {
- result = CreatePlanForDirectMaterialization(queryComponents.LastSegmentType);
- }
- else
- {
- result = ProjectionPlanCompiler.CompilePlan(projection, queryComponents.NormalizerRewrites);
- result.LastSegmentType = queryComponents.LastSegmentType;
- }
-
- return result;
- }
-
- private static ProjectionPlan CreatePlanForDirectMaterialization(Type lastSegmentType)
- {
- ProjectionPlan result = new ProjectionPlan();
- result.Plan = AtomMaterializerInvoker.DirectMaterializePlan;
- result.ProjectedType = lastSegmentType;
- result.LastSegmentType = lastSegmentType;
- return result;
- }
-
- private static ProjectionPlan CreatePlanForShallowMaterialization(Type lastSegmentType)
- {
- ProjectionPlan result = new ProjectionPlan();
- result.Plan = AtomMaterializerInvoker.ShallowMaterializePlan;
- result.ProjectedType = lastSegmentType;
- result.LastSegmentType = lastSegmentType;
- return result;
- }
-
- private static Action<object, object> GetAddToCollectionDelegate(Type listType)
- {
- Debug.Assert(listType != null, "listType != null");
-
- Type listElementType;
- MethodInfo addMethod = ClientType.GetAddToCollectionMethod(listType, out listElementType);
- ParameterExpression list = Expression.Parameter(typeof(object), "list");
- ParameterExpression item = Expression.Parameter(typeof(object), "element");
- Expression body = Expression.Call(Expression.Convert(list, listType), addMethod, Expression.Convert(item, listElementType));
- #if ASTORIA_LIGHT
- LambdaExpression lambda = ExpressionHelpers.CreateLambda(body, list, item);
- #else
- LambdaExpression lambda = Expression.Lambda(body, list, item);
- #endif
- return (Action<object, object>)lambda.Compile();
- }
-
- private static object GetOrCreateCollectionProperty(object instance, ClientType.ClientProperty property, Type collectionType)
- {
- Debug.Assert(instance != null, "instance != null");
- Debug.Assert(property != null, "property != null");
- Debug.Assert(property.CollectionType != null, "property.CollectionType != null -- otherwise property isn't a collection");
-
- object result;
- result = property.GetValue(instance);
- if (result == null)
- {
- if (collectionType == null)
- {
- collectionType = property.PropertyType;
- if (collectionType.IsInterface)
- {
- collectionType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(property.CollectionType);
- }
- }
-
- result = Activator.CreateInstance(collectionType);
- property.SetValue(instance, result, property.PropertyName, false );
- }
-
- Debug.Assert(result != null, "result != null -- otherwise GetOrCreateCollectionProperty didn't fall back to creation");
- return result;
- }
-
- private static void MaterializeToList(
- AtomMaterializer materializer,
- IEnumerable list,
- Type nestedExpectedType,
- IEnumerable<AtomEntry> entries)
- {
- Debug.Assert(materializer != null, "materializer != null");
- Debug.Assert(list != null, "list != null");
-
- Action<object, object> addMethod = GetAddToCollectionDelegate(list.GetType());
- foreach (AtomEntry feedEntry in entries)
- {
- if (!feedEntry.EntityHasBeenResolved)
- {
- materializer.Materialize(feedEntry, nestedExpectedType, false);
- }
-
- addMethod(list, feedEntry.ResolvedObject);
- }
- }
-
- private static bool MaterializeDataValue(Type type, AtomContentProperty atomProperty, DataServiceContext context)
- {
- Debug.Assert(type != null, "type != null");
- Debug.Assert(atomProperty != null, "atomProperty != null");
- Debug.Assert(context != null, "context != null");
-
- string propertyTypeName = atomProperty.TypeName;
- string propertyValueText = atomProperty.Text;
-
- ClientType nestedElementType = null;
- Type underlyingType = Nullable.GetUnderlyingType(type) ?? type;
- bool knownType = ClientConvert.IsKnownType(underlyingType);
- if (!knownType)
- {
- nestedElementType = MaterializeAtom.GetEntryClientType(propertyTypeName, context, type, true);
- Debug.Assert(nestedElementType != null, "nestedElementType != null -- otherwise ReadTypeAttribute (or someone!) should throw");
- knownType = ClientConvert.IsKnownType(nestedElementType.ElementType);
- }
-
- if (knownType)
- {
- if (atomProperty.IsNull)
- {
- if (!ClientType.CanAssignNull(type))
- {
- throw new InvalidOperationException(Strings.AtomMaterializer_CannotAssignNull(atomProperty.Name, type.FullName));
- }
-
- atomProperty.MaterializedValue = null;
- return true;
- }
- else
- {
- object value = propertyValueText;
- if (propertyValueText != null)
- {
- value = ClientConvert.ChangeType(propertyValueText, (null != nestedElementType ? nestedElementType.ElementType : underlyingType));
- }
-
- atomProperty.MaterializedValue = value;
- return true;
- }
- }
-
- return false;
- }
-
- private static void MaterializeDataValues(
- ClientType actualType,
- List<AtomContentProperty> values,
- bool ignoreMissingProperties,
- DataServiceContext context)
- {
- Debug.Assert(actualType != null, "actualType != null");
- Debug.Assert(values != null, "values != null");
- Debug.Assert(context != null, "context != null");
-
- foreach (var atomProperty in values)
- {
- string propertyName = atomProperty.Name;
-
- var property = actualType.GetProperty(propertyName, ignoreMissingProperties); // may throw
- if (property == null)
- {
- continue;
- }
-
- if (atomProperty.Feed == null && atomProperty.Entry == null)
- {
- bool materialized = MaterializeDataValue(property.NullablePropertyType, atomProperty, context);
- if (!materialized && property.CollectionType != null)
- {
- throw Error.NotSupported(Strings.ClientType_CollectionOfNonEntities);
- }
- }
- }
- }
-
- private static void ApplyDataValue(ClientType type, AtomContentProperty property, bool ignoreMissingProperties, DataServiceContext context, object instance)
- {
- Debug.Assert(type != null, "type != null");
- Debug.Assert(property != null, "property != null");
- Debug.Assert(context != null, "context != null");
- Debug.Assert(instance != null, "instance != context");
-
- var prop = type.GetProperty(property.Name, ignoreMissingProperties);
- if (prop == null)
- {
- return;
- }
-
- if (property.Properties != null)
- {
- if (prop.IsKnownType ||
- ClientConvert.IsKnownType(MaterializeAtom.GetEntryClientType(property.TypeName, context, prop.PropertyType, true).ElementType))
- {
- throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
- }
-
- bool needToSet = false;
- ClientType complexType = ClientType.Create(prop.PropertyType);
- object complexInstance = prop.GetValue(instance);
- if (complexInstance == null)
- {
- complexInstance = complexType.CreateInstance();
- needToSet = true;
- }
-
- MaterializeDataValues(complexType, property.Properties, ignoreMissingProperties, context);
- ApplyDataValues(complexType, property.Properties, ignoreMissingProperties, context, complexInstance);
-
- if (needToSet)
- {
- prop.SetValue(instance, complexInstance, property.Name, true );
- }
- }
- else
- {
- prop.SetValue(instance, property.MaterializedValue, property.Name, true );
- }
- }
-
- private static void ApplyDataValues(ClientType type, IEnumerable<AtomContentProperty> properties, bool ignoreMissingProperties, DataServiceContext context, object instance)
- {
- Debug.Assert(type != null, "type != null");
- Debug.Assert(properties != null, "properties != null");
- Debug.Assert(context != null, "properties != context");
- Debug.Assert(instance != null, "instance != context");
-
- foreach (var p in properties)
- {
- ApplyDataValue(type, p, ignoreMissingProperties, context, instance);
- }
- }
-
- private static void SetValueOnPath(List<AtomContentProperty> values, string path, string value, string typeName)
- {
- Debug.Assert(values != null, "values != null");
- Debug.Assert(path != null, "path != null");
-
- bool existing = true;
- AtomContentProperty property = null;
- foreach (string step in path.Split('/'))
- {
- if (values == null)
- {
- Debug.Assert(property != null, "property != null -- if values is null then this isn't the first step");
- property.EnsureProperties();
- values = property.Properties;
- }
-
- property = values.Where(v => v.Name == step).FirstOrDefault();
- if (property == null)
- {
- AtomContentProperty newProperty = new AtomContentProperty();
- existing = false;
- newProperty.Name = step;
- values.Add(newProperty);
- property = newProperty;
- }
- else
- {
- if (property.IsNull)
- {
- return;
- }
- }
-
- values = property.Properties;
- }
-
- Debug.Assert(property != null, "property != null -- property path should have at least one segment");
-
- if (existing == false)
- {
- property.TypeName = typeName;
- property.Text = value;
- }
- }
-
- private static void ApplyEntityPropertyMappings(AtomEntry entry, ClientType entryType)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(entry.Tag is XElement, "entry.Tag is XElement");
- 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)?");
- Debug.Assert(!entry.EntityPropertyMappingsApplied, "!entry.EntityPropertyMappingsApplied -- EPM should happen only once per entry");
-
- if (entryType.HasEntityPropertyMappings)
- {
- XElement entryElement = entry.Tag as XElement;
- Debug.Assert(entryElement != null, "entryElement != null");
- ApplyEntityPropertyMappings(entry, entryElement, entryType.EpmTargetTree.SyndicationRoot);
- ApplyEntityPropertyMappings(entry, entryElement, entryType.EpmTargetTree.NonSyndicationRoot);
- }
-
- entry.EntityPropertyMappingsApplied = true;
- }
-
- private static void ApplyEntityPropertyMappings(AtomEntry entry, XElement entryElement, EpmTargetPathSegment target)
- {
- Debug.Assert(target != null, "target != null");
- Debug.Assert(!target.HasContent, "!target.HasContent");
-
- Stack<System.Data.Services.Common.EpmTargetPathSegment> segments = new Stack<System.Data.Services.Common.EpmTargetPathSegment>();
- Stack<XElement> elements = new Stack<XElement>();
-
- segments.Push(target);
- elements.Push(entryElement);
-
- while (segments.Count > 0)
- {
- System.Data.Services.Common.EpmTargetPathSegment segment = segments.Pop();
- XElement element = elements.Pop();
- if (segment.HasContent)
- {
- var node = element.Nodes().Where(n => n.NodeType == XmlNodeType.Text || n.NodeType == XmlNodeType.SignificantWhitespace).FirstOrDefault();
- string elementValue = (node == null) ? null : ((XText)node).Value;
- Debug.Assert(segment.EpmInfo != null, "segment.EpmInfo != null -- otherwise segment.HasValue should be false");
-
- string path = segment.EpmInfo.Attribute.SourcePath;
- string typeName = (string)element.Attribute(XName.Get(XmlConstants.AtomTypeAttributeName, XmlConstants.DataWebMetadataNamespace));
-
- SetValueOnPath(entry.DataValues, path, elementValue, typeName);
- }
-
- foreach (var item in segment.SubSegments)
- {
- if (item.IsAttribute)
- {
- string localName = item.SegmentName.Substring(1);
- var attribute = element.Attribute(XName.Get(localName, item.SegmentNamespaceUri));
- if (attribute != null)
- {
- SetValueOnPath(entry.DataValues, item.EpmInfo.Attribute.SourcePath, attribute.Value, null);
- }
- }
- else
- {
- var child = element.Element(XName.Get(item.SegmentName, item.SegmentNamespaceUri));
- if (child != null)
- {
- segments.Push(item);
- elements.Push(child);
- }
- }
- }
-
- Debug.Assert(segments.Count == elements.Count, "segments.Count == elements.Count -- otherwise they're out of sync");
- }
- }
-
- private static AtomContentProperty GetPropertyOrThrow(List<AtomContentProperty> properties, string propertyName)
- {
- AtomContentProperty atomProperty = null;
- if (properties != null)
- {
- atomProperty = properties.Where(p => p.Name == propertyName).FirstOrDefault();
- }
-
- if (atomProperty == null)
- {
- throw new InvalidOperationException(Strings.AtomMaterializer_PropertyMissing(propertyName));
- }
-
- Debug.Assert(atomProperty != null, "atomProperty != null");
- return atomProperty;
- }
-
- private static AtomContentProperty GetPropertyOrThrow(AtomEntry entry, string propertyName)
- {
- AtomContentProperty atomProperty = null;
- var properties = entry.DataValues;
- if (properties != null)
- {
- atomProperty = properties.Where(p => p.Name == propertyName).FirstOrDefault();
- }
-
- if (atomProperty == null)
- {
- throw new InvalidOperationException(Strings.AtomMaterializer_PropertyMissingFromEntry(propertyName, entry.Identity));
- }
-
- Debug.Assert(atomProperty != null, "atomProperty != null");
- return atomProperty;
- }
-
- private void MergeLists(AtomEntry entry, ClientType.ClientProperty property, IEnumerable list, Uri nextLink, ProjectionPlan plan)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null");
- Debug.Assert(property != null, "property != null");
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(plan != null || nextLink == null, "plan != null || nextLink == null");
-
- if (entry.ShouldUpdateFromPayload &&
- property.NullablePropertyType == list.GetType() &&
- property.GetValue(entry.ResolvedObject) == null)
- {
- property.SetValue(entry.ResolvedObject, list, property.PropertyName, false );
- this.FoundNextLinkForCollection(list, nextLink, plan);
-
- foreach (object item in list)
- {
- this.log.AddedLink(entry, property.PropertyName, item);
- }
-
- return;
- }
-
- this.ApplyItemsToCollection(entry, property, list, nextLink, plan);
- }
-
- private bool TryResolveAsTarget(AtomEntry entry)
- {
- if (entry.ResolvedObject == null)
- {
- return false;
- }
-
- Debug.Assert(
- entry.ResolvedObject == this.TargetInstance,
- "entry.ResolvedObject == this.TargetInstance -- otherwise there we ResolveOrCreateInstance more than once on the same entry");
- Debug.Assert(
- this.mergeOption == MergeOption.OverwriteChanges || this.mergeOption == MergeOption.PreserveChanges,
- "MergeOption.OverwriteChanges and MergeOption.PreserveChanges are the only expected values during SaveChanges");
- entry.ActualType = ClientType.Create(entry.ResolvedObject.GetType());
- this.log.FoundTargetInstance(entry);
- entry.ShouldUpdateFromPayload = this.mergeOption == MergeOption.PreserveChanges ? false : true;
- entry.EntityHasBeenResolved = true;
- return true;
- }
-
- private bool TryResolveFromContext(AtomEntry entry, Type expectedEntryType)
- {
- bool tracking = this.mergeOption != MergeOption.NoTracking;
- if (tracking)
- {
- EntityStates state;
- entry.ResolvedObject = this.context.TryGetEntity(entry.Identity, entry.ETagText, this.mergeOption, out state);
- if (entry.ResolvedObject != null)
- {
- if (!expectedEntryType.IsInstanceOfType(entry.ResolvedObject))
- {
- throw Error.InvalidOperation(Strings.Deserialize_Current(expectedEntryType, entry.ResolvedObject.GetType()));
- }
-
- entry.ActualType = ClientType.Create(entry.ResolvedObject.GetType());
- entry.EntityHasBeenResolved = true;
-
- entry.ShouldUpdateFromPayload =
- this.mergeOption == MergeOption.OverwriteChanges ||
- (this.mergeOption == MergeOption.PreserveChanges && state == EntityStates.Unchanged) ||
- (this.mergeOption == MergeOption.PreserveChanges && state == EntityStates.Deleted);
- this.log.FoundExistingInstance(entry);
-
- return true;
- }
- }
-
- return false;
- }
-
- private void ResolveByCreatingWithType(AtomEntry entry, Type type)
- {
- Debug.Assert(
- entry.ResolvedObject == null,
- "entry.ResolvedObject == null -- otherwise we're about to overwrite - should never be called");
- entry.ActualType = ClientType.Create(type);
- entry.ResolvedObject = Activator.CreateInstance(type);
- entry.CreatedByMaterializer = true;
- entry.ShouldUpdateFromPayload = true;
- entry.EntityHasBeenResolved = true;
- this.log.CreatedInstance(entry);
- }
-
- private void ResolveByCreating(AtomEntry entry, Type expectedEntryType)
- {
- Debug.Assert(
- entry.ResolvedObject == null,
- "entry.ResolvedObject == null -- otherwise we're about to overwrite - should never be called");
-
- ClientType actualType = MaterializeAtom.GetEntryClientType(entry.TypeName, this.context, expectedEntryType, true);
-
- Debug.Assert(actualType != null, "actualType != null -- otherwise ClientType.Create returned a null value");
- this.ResolveByCreatingWithType(entry, actualType.ElementType);
- }
-
- private bool TryResolveAsCreated(AtomEntry entry)
- {
- AtomEntry existingEntry;
- if (!this.log.TryResolve(entry, out existingEntry))
- {
- return false;
- }
-
- Debug.Assert(
- existingEntry.ResolvedObject != null,
- "existingEntry.ResolvedObject != null -- how did it get there otherwise?");
- entry.ActualType = existingEntry.ActualType;
- entry.ResolvedObject = existingEntry.ResolvedObject;
- entry.CreatedByMaterializer = existingEntry.CreatedByMaterializer;
- entry.ShouldUpdateFromPayload = existingEntry.ShouldUpdateFromPayload;
- entry.EntityHasBeenResolved = true;
- return true;
- }
-
- private void ResolveOrCreateInstance(AtomEntry entry, Type expectedEntryType)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(expectedEntryType != null, "expectedEntryType != null");
- Debug.Assert(entry.EntityHasBeenResolved == false, "entry.EntityHasBeenResolved == false");
-
- if (!this.TryResolveAsTarget(entry))
- {
- if (entry.Identity == null)
- {
- throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement);
- }
-
- if (!this.TryResolveAsCreated(entry))
- {
- if (!this.TryResolveFromContext(entry, expectedEntryType))
- {
- this.ResolveByCreating(entry, expectedEntryType);
- }
- }
- }
-
- Debug.Assert(entry.ActualType != null, "entry.ActualType != null");
- Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null");
- Debug.Assert(entry.EntityHasBeenResolved, "entry.EntityHasBeenResolved");
-
- return;
- }
-
- private void ApplyFeedToCollection(
- AtomEntry entry,
- ClientType.ClientProperty property,
- AtomFeed feed,
- bool includeLinks)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(property != null, "property != null");
- Debug.Assert(feed != null, "feed != null");
-
- ClientType collectionType = ClientType.Create(property.CollectionType);
- foreach (AtomEntry feedEntry in feed.Entries)
- {
- this.Materialize(feedEntry, collectionType.ElementType, includeLinks);
- }
-
- ProjectionPlan continuationPlan = includeLinks ? CreatePlanForDirectMaterialization(property.CollectionType) : CreatePlanForShallowMaterialization(property.CollectionType);
- this.ApplyItemsToCollection(entry, property, feed.Entries.Select(e => e.ResolvedObject), feed.NextLink, continuationPlan);
- }
-
- private void ApplyItemsToCollection(
- AtomEntry entry,
- ClientType.ClientProperty property,
- IEnumerable items,
- Uri nextLink,
- ProjectionPlan continuationPlan)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(property != null, "property != null");
- Debug.Assert(items != null, "items != null");
-
- object collection = entry.ShouldUpdateFromPayload ? GetOrCreateCollectionProperty(entry.ResolvedObject, property, null) : null;
- ClientType collectionType = ClientType.Create(property.CollectionType);
- foreach (object item in items)
- {
- if (!collectionType.ElementType.IsAssignableFrom(item.GetType()))
- {
- string message = Strings.AtomMaterializer_EntryIntoCollectionMismatch(
- item.GetType().FullName,
- collectionType.ElementType.FullName);
- throw new InvalidOperationException(message);
- }
-
- if (entry.ShouldUpdateFromPayload)
- {
- property.SetValue(collection, item, property.PropertyName, true );
- this.log.AddedLink(entry, property.PropertyName, item);
- }
- }
-
- if (entry.ShouldUpdateFromPayload)
- {
- this.FoundNextLinkForCollection(collection as IEnumerable, nextLink, continuationPlan);
- }
- else
- {
- this.FoundNextLinkForUnmodifiedCollection(property.GetValue(entry.ResolvedObject) as IEnumerable);
- }
-
- if (this.mergeOption == MergeOption.OverwriteChanges || this.mergeOption == MergeOption.PreserveChanges)
- {
- var itemsToRemove =
- from x in this.context.GetLinks(entry.ResolvedObject, property.PropertyName)
- where MergeOption.OverwriteChanges == this.mergeOption || EntityStates.Added != x.State
- select x.Target;
- itemsToRemove = itemsToRemove.Except(EnumerateAsElementType<object>(items));
- foreach (var item in itemsToRemove)
- {
- if (collection != null)
- {
- property.RemoveValue(collection, item);
- }
-
- this.log.RemovedLink(entry, property.PropertyName, item);
- }
- }
- }
-
- private void FoundNextLinkForCollection(IEnumerable collection, Uri link, ProjectionPlan plan)
- {
- Debug.Assert(plan != null || link == null, "plan != null || link == null");
-
- if (collection != null && !this.nextLinkTable.ContainsKey(collection))
- {
- DataServiceQueryContinuation continuation = DataServiceQueryContinuation.Create(link, plan);
- this.nextLinkTable.Add(collection, continuation);
- Util.SetNextLinkForCollection(collection, continuation);
- }
- }
-
- private void FoundNextLinkForUnmodifiedCollection(IEnumerable collection)
- {
- if (collection != null && !this.nextLinkTable.ContainsKey(collection))
- {
- this.nextLinkTable.Add(collection, null);
- }
- }
-
- private void Materialize(AtomEntry entry, Type expectedEntryType, bool includeLinks)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(entry.DataValues != null, "entry.DataValues != null -- otherwise not correctly initialized");
- Debug.Assert(
- entry.ResolvedObject == null || entry.ResolvedObject == this.targetInstance,
- "entry.ResolvedObject == null || entry.ResolvedObject == this.targetInstance -- otherwise getting called twice");
- Debug.Assert(expectedEntryType != null, "expectedType != null");
-
- this.ResolveOrCreateInstance(entry, expectedEntryType);
- Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise ResolveOrCreateInstnace didn't do its job");
-
- this.MaterializeResolvedEntry(entry, includeLinks);
- }
-
- private void MaterializeResolvedEntry(AtomEntry entry, bool includeLinks)
- {
- Debug.Assert(entry != null, "entry != null");
- Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!");
-
- ClientType actualType = entry.ActualType;
-
- if (!entry.EntityPropertyMappingsApplied)
- {
- ApplyEntityPropertyMappings(entry, entry.ActualType);
- }
-
- MaterializeDataValues(actualType, entry.DataValues, this.ignoreMissingProperties, this.context);
-
- foreach (var e in entry.DataValues)
- {
- var prop = actualType.GetProperty(e.Name, this.ignoreMissingProperties);
- if (prop == null)
- {
- continue;
- }
-
- if (entry.ShouldUpdateFromPayload == false && e.Entry == null && e.Feed == null)
- {
- continue;
- }
-
- if (!includeLinks && (e.Entry != null || e.Feed != null))
- {
- continue;
- }
-
- ValidatePropertyMatch(prop, e);
-
- AtomFeed feedValue = e.Feed;
- if (feedValue != null)
- {
- Debug.Assert(includeLinks, "includeLinks -- otherwise we shouldn't be materializing this entry");
- this.ApplyFeedToCollection(entry, prop, feedValue, includeLinks);
- }
- else if (e.Entry != null)
- {
- if (!e.IsNull)
- {
- Debug.Assert(includeLinks, "includeLinks -- otherwise we shouldn't be materializing this entry");
- this.Materialize(e.Entry, prop.PropertyType, includeLinks);
- }
-
- if (entry.ShouldUpdateFromPayload)
- {
- prop.SetValue(entry.ResolvedObject, e.Entry.ResolvedObject, e.Name, true );
- this.log.SetLink(entry, prop.PropertyName, e.Entry.ResolvedObject);
- }
- }
- else
- {
- Debug.Assert(entry.ShouldUpdateFromPayload, "entry.ShouldUpdateFromPayload -- otherwise we're about to set a property we shouldn't");
- ApplyDataValue(actualType, e, this.ignoreMissingProperties, this.context, entry.ResolvedObject);
- }
- }
-
- Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise we didn't do any useful work");
- if (this.materializedObjectCallback != null)
- {
- this.materializedObjectCallback(entry.Tag, entry.ResolvedObject);
- }
- }
-
- #endregion Private methods.
- }
- }