From 7f2f53992d7d9a02daf3f01adaa97d31fbf2162a Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Fri, 9 Sep 2022 22:51:14 +0300 Subject: [PATCH 01/16] Replace ConditionalWeakTable with MaterializerContext.AnnotationsCache --- .../AtomMaterializerLog.cs | 11 +++-- .../CollectionValueMaterializationPolicy.cs | 2 +- .../Materialization/EntityTrackingAdapter.cs | 4 +- .../EntryValueMaterializationPolicy.cs | 14 +++--- .../EnumValueMaterializationPolicy.cs | 4 +- .../FeedAndEntryMaterializerAdapter.cs | 24 +++++----- .../IODataMaterializerContext.cs | 2 + .../MaterializerAnnotationsCache.cs | 44 +++++++++++++++++++ .../Materialization/MaterializerEntry.cs | 10 ++--- .../Materialization/MaterializerFeed.cs | 10 ++--- .../MaterializerNavigationLink.cs | 16 +++---- .../ODataEntityMaterializer.cs | 34 +++++++------- .../ODataEntityMaterializerInvoker.cs | 27 +++++++----- .../Materialization/ODataItemExtensions.cs | 24 +++++----- .../Materialization/ODataMaterializer.cs | 2 +- .../ODataMaterializerContext.cs | 3 ++ .../ODataReaderEntityMaterializer.cs | 8 ++-- .../StructuralValueMaterializationPolicy.cs | 6 +-- .../MaterializeFromAtom.cs | 2 +- .../ODataAnnotatableExtensions.cs | 32 +++++++------- src/Microsoft.OData.Client/SaveResult.cs | 3 +- 21 files changed, 174 insertions(+), 108 deletions(-) create mode 100644 src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs diff --git a/src/Microsoft.OData.Client/AtomMaterializerLog.cs b/src/Microsoft.OData.Client/AtomMaterializerLog.cs index 6a89587bbf..0b79b25428 100644 --- a/src/Microsoft.OData.Client/AtomMaterializerLog.cs +++ b/src/Microsoft.OData.Client/AtomMaterializerLog.cs @@ -46,6 +46,8 @@ internal class AtomMaterializerLog /// <summary>Target instance to refresh.</summary> private object insertRefreshObject; + private IODataMaterializerContext materializerContext; + #endregion Private fields #region Constructors @@ -59,7 +61,7 @@ internal class AtomMaterializerLog /// <remarks> /// Note that the merge option can't be changed. /// </remarks> - internal AtomMaterializerLog(MergeOption mergeOption, ClientEdmModel model, EntityTrackerBase entityTracker) + internal AtomMaterializerLog(MergeOption mergeOption, ClientEdmModel model, EntityTrackerBase entityTracker, IODataMaterializerContext materializerContext) { Debug.Assert(model != null, "model != null"); Debug.Assert(entityTracker != null, "entityTracker != null"); @@ -70,6 +72,7 @@ internal AtomMaterializerLog(MergeOption mergeOption, ClientEdmModel model, Enti this.entityTracker = entityTracker; this.identityStack = new Dictionary<Uri, ODataResource>(EqualityComparer<Uri>.Default); this.links = new List<LinkDescriptor>(); + this.materializerContext = materializerContext; } #endregion Constructors @@ -179,7 +182,7 @@ internal void ApplyToContext() foreach (KeyValuePair<Uri, ODataResource> entity in this.identityStack) { // Try to attach the entity descriptor got from materializer, if one already exists, get the existing reference instead. - MaterializerEntry entry = MaterializerEntry.GetEntry(entity.Value); + MaterializerEntry entry = MaterializerEntry.GetEntry(entity.Value, this.materializerContext); bool mergeEntityDescriptorInfo = entry.CreatedByMaterializer || entry.ResolvedObject == this.insertRefreshObject || @@ -309,7 +312,7 @@ internal bool TryResolve(MaterializerEntry entry, out MaterializerEntry existing if (this.identityStack.TryGetValue(entry.Id, out existingODataEntry)) { - existingEntry = MaterializerEntry.GetEntry(existingODataEntry); + existingEntry = MaterializerEntry.GetEntry(existingODataEntry, this.materializerContext); return true; } @@ -321,7 +324,7 @@ internal bool TryResolve(MaterializerEntry entry, out MaterializerEntry existing this.entityTracker.TryGetEntity(entry.Id, out state); if (state == EntityStates.Unchanged) { - existingEntry = MaterializerEntry.GetEntry(existingODataEntry); + existingEntry = MaterializerEntry.GetEntry(existingODataEntry, this.materializerContext); return true; } else diff --git a/src/Microsoft.OData.Client/Materialization/CollectionValueMaterializationPolicy.cs b/src/Microsoft.OData.Client/Materialization/CollectionValueMaterializationPolicy.cs index 5406d6234e..dd559d54ce 100644 --- a/src/Microsoft.OData.Client/Materialization/CollectionValueMaterializationPolicy.cs +++ b/src/Microsoft.OData.Client/Materialization/CollectionValueMaterializationPolicy.cs @@ -134,7 +134,7 @@ internal void ApplyCollectionDataValues( addValueToBackingICollectionInstance, isElementNullable); - collectionProperty.SetMaterializedValue(collectionInstance); + collectionProperty.SetMaterializedValue(collectionInstance, this.materializerContext); } /// <summary> diff --git a/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs b/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs index 1a68c1ee68..be2e6e2bbf 100644 --- a/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs +++ b/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs @@ -27,9 +27,9 @@ internal class EntityTrackingAdapter /// <param name="mergeOption">The merge option.</param> /// <param name="model">The model.</param> /// <param name="context">The context.</param> - internal EntityTrackingAdapter(EntityTrackerBase entityTracker, MergeOption mergeOption, ClientEdmModel model, DataServiceContext context) + internal EntityTrackingAdapter(EntityTrackerBase entityTracker, MergeOption mergeOption, ClientEdmModel model, DataServiceContext context, IODataMaterializerContext materializerContext) { - this.MaterializationLog = new AtomMaterializerLog(mergeOption, model, entityTracker); + this.MaterializationLog = new AtomMaterializerLog(mergeOption, model, entityTracker, materializerContext); this.MergeOption = mergeOption; this.EntityTracker = entityTracker; this.Model = model; diff --git a/src/Microsoft.OData.Client/Materialization/EntryValueMaterializationPolicy.cs b/src/Microsoft.OData.Client/Materialization/EntryValueMaterializationPolicy.cs index 7172a50738..1d58a28020 100644 --- a/src/Microsoft.OData.Client/Materialization/EntryValueMaterializationPolicy.cs +++ b/src/Microsoft.OData.Client/Materialization/EntryValueMaterializationPolicy.cs @@ -534,11 +534,11 @@ private void ApplyFeedToCollection( ClientEdmModel edmModel = this.MaterializerContext.Model; ClientTypeAnnotation collectionType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(property.ResourceSetItemType)); - IEnumerable<ODataResource> entries = MaterializerFeed.GetFeed(feed).Entries; + IEnumerable<ODataResource> entries = MaterializerFeed.GetFeed(feed, this.MaterializerContext).Entries; foreach (ODataResource feedEntry in entries) { - this.Materialize(MaterializerEntry.GetEntry(feedEntry), collectionType.ElementType, includeLinks); + this.Materialize(MaterializerEntry.GetEntry(feedEntry, this.MaterializerContext), collectionType.ElementType, includeLinks); } ProjectionPlan continuationPlan = includeLinks ? @@ -548,7 +548,7 @@ private void ApplyFeedToCollection( this.ApplyItemsToCollection( entry, property, - entries.Select(e => MaterializerEntry.GetEntry(e).ResolvedObject), + entries.Select(e => MaterializerEntry.GetEntry(e, this.MaterializerContext).ResolvedObject), feed.NextPageLink, continuationPlan, false); @@ -591,7 +591,7 @@ private void MaterializeResolvedEntry(MaterializerEntry entry, bool includeLinks foreach (ODataNestedResourceInfo link in entry.NestedResourceInfos) { - MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link); + MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link, this.MaterializerContext); if (linkState == null) { @@ -722,7 +722,7 @@ private void MaterializeDynamicProperty(MaterializerEntry entry, ODataNestedReso return; } - MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link); + MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link, this.MaterializerContext); if (linkState == null || (linkState.Entry == null && linkState.Feed == null)) { return; @@ -754,10 +754,10 @@ private void MaterializeDynamicProperty(MaterializerEntry entry, ODataNestedReso Type collectionType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(new Type[] { collectionItemType }); IList collection = (IList)Util.ActivatorCreateInstance(collectionType); - IEnumerable<ODataResource> feedEntries = MaterializerFeed.GetFeed(linkState.Feed).Entries; + IEnumerable<ODataResource> feedEntries = MaterializerFeed.GetFeed(linkState.Feed, this.MaterializerContext).Entries; foreach (ODataResource feedEntry in feedEntries) { - MaterializerEntry linkEntry = MaterializerEntry.GetEntry(feedEntry); + MaterializerEntry linkEntry = MaterializerEntry.GetEntry(feedEntry, this.MaterializerContext); this.Materialize(linkEntry, collectionItemType, false /*includeLinks*/); collection.Add(linkEntry.ResolvedObject); } diff --git a/src/Microsoft.OData.Client/Materialization/EnumValueMaterializationPolicy.cs b/src/Microsoft.OData.Client/Materialization/EnumValueMaterializationPolicy.cs index 21975e8160..e5a6f5e314 100644 --- a/src/Microsoft.OData.Client/Materialization/EnumValueMaterializationPolicy.cs +++ b/src/Microsoft.OData.Client/Materialization/EnumValueMaterializationPolicy.cs @@ -40,9 +40,9 @@ public object MaterializeEnumTypeProperty(Type valueType, ODataProperty property object materializedValue = null; ODataEnumValue value = property.Value as ODataEnumValue; this.MaterializeODataEnumValue(valueType, value.TypeName, value.Value, () => "TODO: Is this reachable?", out materializedValue); - if (!property.HasMaterializedValue()) + if (!property.HasMaterializedValue(this.context)) { - property.SetMaterializedValue(materializedValue); + property.SetMaterializedValue(materializedValue, this.context); } return materializedValue; diff --git a/src/Microsoft.OData.Client/Materialization/FeedAndEntryMaterializerAdapter.cs b/src/Microsoft.OData.Client/Materialization/FeedAndEntryMaterializerAdapter.cs index 88a9e5dbf3..19815f4299 100644 --- a/src/Microsoft.OData.Client/Materialization/FeedAndEntryMaterializerAdapter.cs +++ b/src/Microsoft.OData.Client/Materialization/FeedAndEntryMaterializerAdapter.cs @@ -39,6 +39,8 @@ internal class FeedAndEntryMaterializerAdapter /// <summary>The current entry.</summary> private ODataResource currentEntry; + private IODataMaterializerContext materializerContext; + /// <summary> /// Initializes a new instance of the <see cref="FeedAndEntryMaterializerAdapter"/> class. /// </summary> @@ -46,8 +48,8 @@ internal class FeedAndEntryMaterializerAdapter /// <param name="reader">The reader.</param> /// <param name="model">The model.</param> /// <param name="mergeOption">The mergeOption.</param> - internal FeedAndEntryMaterializerAdapter(ODataMessageReader messageReader, ODataReaderWrapper reader, ClientEdmModel model, MergeOption mergeOption) - : this(ODataUtils.GetReadFormat(messageReader), reader, model, mergeOption) + internal FeedAndEntryMaterializerAdapter(ODataMessageReader messageReader, ODataReaderWrapper reader, ClientEdmModel model, MergeOption mergeOption, IODataMaterializerContext materializerContext) + : this(ODataUtils.GetReadFormat(messageReader), reader, model, mergeOption, materializerContext) { } @@ -58,7 +60,7 @@ internal FeedAndEntryMaterializerAdapter(ODataMessageReader messageReader, OData /// <param name="reader">The reader.</param> /// <param name="model">The model.</param> /// <param name="mergeOption">The mergeOption.</param> - internal FeedAndEntryMaterializerAdapter(ODataFormat odataFormat, ODataReaderWrapper reader, ClientEdmModel model, MergeOption mergeOption) + internal FeedAndEntryMaterializerAdapter(ODataFormat odataFormat, ODataReaderWrapper reader, ClientEdmModel model, MergeOption mergeOption, IODataMaterializerContext materializerContext) { this.readODataFormat = odataFormat; this.clientEdmModel = model; @@ -67,6 +69,7 @@ internal FeedAndEntryMaterializerAdapter(ODataFormat odataFormat, ODataReaderWra this.currentEntry = null; this.currentFeed = null; this.feedEntries = null; + this.materializerContext = materializerContext; } /// <summary> @@ -105,7 +108,7 @@ public long GetCountValue(bool readIfNoFeed) { if (this.currentFeed == null && this.currentEntry == null && readIfNoFeed && this.TryReadFeed(true, out this.currentFeed)) { - this.feedEntries = MaterializerFeed.GetFeed(this.currentFeed).Entries.GetEnumerator(); + this.feedEntries = MaterializerFeed.GetFeed(this.currentFeed, this.materializerContext).Entries.GetEnumerator(); } if (this.currentFeed != null && this.currentFeed.Count.HasValue) @@ -152,7 +155,7 @@ public bool Read() this.currentFeed = feed; if (this.currentFeed != null) { - this.feedEntries = MaterializerFeed.GetFeed(this.currentFeed).Entries.GetEnumerator(); + this.feedEntries = MaterializerFeed.GetFeed(this.currentFeed, this.materializerContext).Entries.GetEnumerator(); // Try to read the first entry. if (!this.feedEntries.MoveNext()) @@ -278,11 +281,11 @@ private ODataResourceSet ReadFeedCore(bool lazy) if (lazy) { - MaterializerFeed.CreateFeed(result, lazyEntries); + MaterializerFeed.CreateFeed(result, lazyEntries, this.materializerContext); } else { - MaterializerFeed.CreateFeed(result, new List<ODataResource>(lazyEntries)); + MaterializerFeed.CreateFeed(result, new List<ODataResource>(lazyEntries), this.materializerContext); } return result; @@ -339,7 +342,8 @@ private MaterializerEntry ReadEntryCore() result, this.readODataFormat, this.mergeOption != MergeOption.NoTracking, - this.clientEdmModel); + this.clientEdmModel, + this.materializerContext); do { @@ -395,12 +399,12 @@ private ODataNestedResourceInfo ReadNestedResourceInfo() { if (feed != null) { - MaterializerNavigationLink.CreateLink(link, feed); + MaterializerNavigationLink.CreateLink(link, feed, this.materializerContext); } else { Debug.Assert(entry != null, "entry != null"); - MaterializerNavigationLink.CreateLink(link, entry); + MaterializerNavigationLink.CreateLink(link, entry, this.materializerContext); } this.ReadAndExpectState(ODataReaderState.NestedResourceInfoEnd); diff --git a/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs b/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs index b28f50fe0b..edb4c7e90d 100644 --- a/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs +++ b/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs @@ -52,5 +52,7 @@ internal interface IODataMaterializerContext /// <param name="clientClrType">The client side CLR type.</param> /// <returns>The resolved EDM type to provide to ODataLib.</returns> IEdmType ResolveExpectedTypeForReading(Type clientClrType); + + MaterializerAnnotationsCache AnnotationsCache { get; } } } diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs new file mode 100644 index 0000000000..1c14b2e349 --- /dev/null +++ b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.OData.Client.Materialization +{ + internal class MaterializerAnnotationsCache + { + private readonly Dictionary<ODataAnnotatable, object> cache = new Dictionary<ODataAnnotatable, object>(ReferenceEqualityComparer<ODataAnnotatable>.Instance); + + public void SetAnnotation<T>(ODataAnnotatable annotatable, T value) where T : class + { + this.cache.Add(annotatable, value); + } + + public T GetAnnotation<T>(ODataAnnotatable annotatable) where T: class + { + if (this.cache.TryGetValue(annotatable, out object value)) + { + Debug.Assert(value is T); + return value as T; + } + + return default(T); + } + } + + internal static class MaterializerContextExtensions + { + public static void SetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable, T value) where T : class + { + context.AnnotationsCache.SetAnnotation(annotatable, value); + } + + public static T GetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable) where T : class + { + return context.AnnotationsCache.GetAnnotation<T>(annotatable); + } + } +} diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs b/src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs index de815eae35..0106ea7ef8 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs @@ -225,12 +225,12 @@ public static MaterializerEntry CreateEmpty() /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param> /// <param name="model">The client model.</param> /// <returns>A new materializer entry.</returns> - public static MaterializerEntry CreateEntry(ODataResource entry, ODataFormat format, bool isTracking, ClientEdmModel model) + public static MaterializerEntry CreateEntry(ODataResource entry, ODataFormat format, bool isTracking, ClientEdmModel model, IODataMaterializerContext materializerContext) { - Debug.Assert(entry.GetAnnotation<MaterializerEntry>() == null, "MaterializerEntry has already been created."); + Debug.Assert(materializerContext.GetAnnotation<MaterializerEntry>(entry) == null, "MaterializerEntry has already been created."); MaterializerEntry materializerEntry = new MaterializerEntry(entry, format, isTracking, model); - entry.SetAnnotation<MaterializerEntry>(materializerEntry); + materializerContext.SetAnnotation<MaterializerEntry>(entry, materializerEntry); return materializerEntry; } @@ -252,9 +252,9 @@ public static MaterializerEntry CreateEntryForLoadProperty(EntityDescriptor desc /// </summary> /// <param name="entry">The ODataResource.</param> /// <returns>The materializer entry</returns> - public static MaterializerEntry GetEntry(ODataResource entry) + public static MaterializerEntry GetEntry(ODataResource entry, IODataMaterializerContext materializerContext) { - return entry.GetAnnotation<MaterializerEntry>(); + return materializerContext.GetAnnotation<MaterializerEntry>(entry); } /// <summary> diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerFeed.cs b/src/Microsoft.OData.Client/Materialization/MaterializerFeed.cs index b29940d832..adf624e3bf 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerFeed.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerFeed.cs @@ -68,16 +68,16 @@ public Uri NextPageLink /// <param name="feed">The feed.</param> /// <param name="entries">The entries.</param> /// <returns>The materializer feed.</returns> - public static MaterializerFeed CreateFeed(ODataResourceSet feed, IEnumerable<ODataResource> entries) + public static MaterializerFeed CreateFeed(ODataResourceSet feed, IEnumerable<ODataResource> entries, IODataMaterializerContext materializerContext) { - Debug.Assert(feed.GetAnnotation<IEnumerable<ODataResource>>() == null, "Feed state has already been created."); + Debug.Assert(materializerContext.GetAnnotation<IEnumerable<ODataResource>>(feed) == null, "Feed state has already been created."); if (entries == null) { entries = Enumerable.Empty<ODataResource>(); } else { - feed.SetAnnotation<IEnumerable<ODataResource>>(entries); + materializerContext.SetAnnotation<IEnumerable<ODataResource>>(feed, entries); } return new MaterializerFeed(feed, entries); @@ -88,9 +88,9 @@ public static MaterializerFeed CreateFeed(ODataResourceSet feed, IEnumerable<ODa /// </summary> /// <param name="feed">The feed.</param> /// <returns>The materializer feed.</returns> - public static MaterializerFeed GetFeed(ODataResourceSet feed) + public static MaterializerFeed GetFeed(ODataResourceSet feed, IODataMaterializerContext materializerContext) { - IEnumerable<ODataResource> entries = feed.GetAnnotation<IEnumerable<ODataResource>>(); + IEnumerable<ODataResource> entries = materializerContext.GetAnnotation<IEnumerable<ODataResource>>(feed); return new MaterializerFeed(feed, entries); } } diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerNavigationLink.cs b/src/Microsoft.OData.Client/Materialization/MaterializerNavigationLink.cs index 40ebd13752..ec4b360b14 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerNavigationLink.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerNavigationLink.cs @@ -64,11 +64,11 @@ public ODataResourceSet Feed /// <param name="link">The link.</param> /// <param name="entry">The entry.</param> /// <returns>The materializer link.</returns> - public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link, MaterializerEntry entry) + public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link, MaterializerEntry entry, IODataMaterializerContext materializerContext) { - Debug.Assert(link.GetAnnotation<MaterializerNavigationLink>() == null, "there should be no MaterializerNestedResourceInfo annotation on the entry link yet"); + Debug.Assert(materializerContext.GetAnnotation<MaterializerNavigationLink>(link) == null, "there should be no MaterializerNestedResourceInfo annotation on the entry link yet"); MaterializerNavigationLink materializedNestedResourceInfo = new MaterializerNavigationLink(link, entry); - link.SetAnnotation<MaterializerNavigationLink>(materializedNestedResourceInfo); + materializerContext.SetAnnotation<MaterializerNavigationLink>(link, materializedNestedResourceInfo); return materializedNestedResourceInfo; } @@ -78,11 +78,11 @@ public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link /// <param name="link">The link.</param> /// <param name="resourceSet">The resource set.</param> /// <returns>The materializer link.</returns> - public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link, ODataResourceSet resourceSet) + public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link, ODataResourceSet resourceSet, IODataMaterializerContext materializerContext) { - Debug.Assert(link.GetAnnotation<MaterializerNavigationLink>() == null, "there should be no MaterializerNestedResourceInfo annotation on the feed link yet"); + Debug.Assert(materializerContext.GetAnnotation<MaterializerNavigationLink>(link) == null, "there should be no MaterializerNestedResourceInfo annotation on the feed link yet"); MaterializerNavigationLink materializedNestedResourceInfo = new MaterializerNavigationLink(link, resourceSet); - link.SetAnnotation<MaterializerNavigationLink>(materializedNestedResourceInfo); + materializerContext.SetAnnotation<MaterializerNavigationLink>(link, materializedNestedResourceInfo); return materializedNestedResourceInfo; } @@ -91,9 +91,9 @@ public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link /// </summary> /// <param name="link">The link.</param> /// <returns>The materializer link.</returns> - public static MaterializerNavigationLink GetLink(ODataNestedResourceInfo link) + public static MaterializerNavigationLink GetLink(ODataNestedResourceInfo link, IODataMaterializerContext materializerContext) { - return link.GetAnnotation<MaterializerNavigationLink>(); + return materializerContext.GetAnnotation<MaterializerNavigationLink>(link); } } } \ No newline at end of file diff --git a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs index 7b978fa569..384a0a5a4f 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs @@ -240,7 +240,8 @@ internal static ProjectionPlan CreatePlanForShallowMaterialization(Type lastSegm internal static bool ProjectionCheckValueForPathIsNull( MaterializerEntry entry, Type expectedType, - ProjectionPath path) + ProjectionPath path, + IODataMaterializerContext materializerContext) { Debug.Assert(path != null, "path != null"); @@ -279,7 +280,7 @@ internal static bool ProjectionCheckValueForPathIsNull( IEdmType expectedEdmType = model.GetOrCreateEdmType(expectedType); ClientPropertyAnnotation property = model.GetClientTypeAnnotation(expectedEdmType).GetProperty(propertyName, UndeclaredPropertyBehavior.ThrowException); - atomProperty = ODataEntityMaterializer.GetPropertyOrThrow(properties, propertyName); + atomProperty = ODataEntityMaterializer.GetPropertyOrThrow(properties, propertyName, materializerContext); EntryValueMaterializationPolicy.ValidatePropertyMatch(property, atomProperty.Link); if (atomProperty.Feed != null) { @@ -348,7 +349,7 @@ internal static IEnumerable ProjectionSelect( // If we are projecting a property defined on a derived type and the entry is of the base type, get property would throw. The user need to check for null in the query. // e.g. Select(p => new MyEmployee { ID = p.ID, Manager = (p as Employee).Manager == null ? null : new MyManager { ID = (p as Employee).Manager.ID } }) - atomProperty = ODataEntityMaterializer.GetPropertyOrThrow(entry.NestedResourceInfos, propertyName); + atomProperty = ODataEntityMaterializer.GetPropertyOrThrow(entry.NestedResourceInfos, propertyName, materializer.MaterializerContext); if (atomProperty.Entry != null) { @@ -358,7 +359,7 @@ internal static IEnumerable ProjectionSelect( } EntryValueMaterializationPolicy.ValidatePropertyMatch(property, atomProperty.Link); - MaterializerFeed sourceFeed = MaterializerFeed.GetFeed(atomProperty.Feed); + MaterializerFeed sourceFeed = MaterializerFeed.GetFeed(atomProperty.Feed, materializer.MaterializerContext); Debug.Assert( sourceFeed.Feed != null, "sourceFeed != null -- otherwise ValidatePropertyMatch should have thrown or property isn't a collection (and should be part of this plan)"); @@ -384,13 +385,13 @@ internal static IEnumerable ProjectionSelect( /// <param name="entry">Entry to get sub-entry from.</param> /// <param name="name">Name of sub-entry.</param> /// <returns>The sub-entry (never null).</returns> - internal static ODataResource ProjectionGetEntry(MaterializerEntry entry, string name) + internal static ODataResource ProjectionGetEntry(MaterializerEntry entry, string name, IODataMaterializerContext materializerContext) { Debug.Assert(entry.Entry != null, "entry != null -- ProjectionGetEntry never returns a null entry, and top-level materialization shouldn't pass one in"); // If we are projecting a property defined on a derived type and the entry is of the base type, get property would throw. The user need to check for null in the query. // e.g. Select(p => new MyEmployee { ID = p.ID, Manager = (p as Employee).Manager == null ? null : new MyManager { ID = (p as Employee).Manager.ID } }) - MaterializerNavigationLink property = ODataEntityMaterializer.GetPropertyOrThrow(entry.NestedResourceInfos, name); + MaterializerNavigationLink property = ODataEntityMaterializer.GetPropertyOrThrow(entry.NestedResourceInfos, name, materializerContext); MaterializerEntry result = property.Entry; if (result == null) { @@ -662,11 +663,11 @@ internal object ProjectionValueForPath(MaterializerEntry entry, Type expectedTyp { EntryValueMaterializationPolicy.ValidatePropertyMatch(property, link); - MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link); + MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link, this.MaterializerContext); if (linkState.Feed != null) { - MaterializerFeed feedValue = MaterializerFeed.GetFeed(linkState.Feed); + MaterializerFeed feedValue = MaterializerFeed.GetFeed(linkState.Feed, this.MaterializerContext); Debug.Assert(segmentIsLeaf, "segmentIsLeaf -- otherwise the path generated traverses a feed, which should be disallowed"); @@ -689,7 +690,7 @@ internal object ProjectionValueForPath(MaterializerEntry entry, Type expectedTyp } IEnumerable list = (IEnumerable)Util.ActivatorCreateInstance(feedType); - MaterializeToList(this, list, nestedExpectedType, feedValue.Entries); + MaterializeToList(this, list, nestedExpectedType, feedValue.Entries, this.MaterializerContext); if (ClientTypeUtil.IsDataServiceCollection(segment.ProjectionType)) { @@ -786,7 +787,7 @@ internal object ProjectionValueForPath(MaterializerEntry entry, Type expectedTyp properties = ODataMaterializer.EmptyProperties; } - result = odataProperty.GetMaterializedValue(); + result = odataProperty.GetMaterializedValue(this.MaterializerContext); // TODO: projection with anonymous type is not supported now. // apply instance annotation for property @@ -847,7 +848,7 @@ internal object ProjectionDynamicValueForPath(MaterializerEntry entry, Type expe this.entryValueMaterializationPolicy.MaterializePrimitiveDataValue(expectedPropertyType, odataProperty); - return odataProperty.GetMaterializedValue(); + return odataProperty.GetMaterializedValue(this.MaterializerContext); } return result; @@ -903,7 +904,7 @@ protected override bool ReadImplementation() Debug.Assert(this.CurrentEntry != null, "Read successfully without finding an entry."); - MaterializerEntry entryAndState = MaterializerEntry.GetEntry(this.CurrentEntry); + MaterializerEntry entryAndState = MaterializerEntry.GetEntry(this.CurrentEntry, this.MaterializerContext); entryAndState.ResolvedObject = this.TargetInstance; this.currentValue = this.materializeEntryPlan.Run(this, this.CurrentEntry, this.ExpectedType); @@ -998,7 +999,8 @@ private static void MaterializeToList( ODataEntityMaterializer materializer, IEnumerable list, Type nestedExpectedType, - IEnumerable<ODataResource> entries) + IEnumerable<ODataResource> entries, + IODataMaterializerContext materializerContext) { Debug.Assert(materializer != null, "materializer != null"); Debug.Assert(list != null, "list != null"); @@ -1006,7 +1008,7 @@ private static void MaterializeToList( Action<object, object> addMethod = ClientTypeUtil.GetAddToCollectionDelegate(list.GetType()); foreach (ODataResource feedEntry in entries) { - MaterializerEntry feedEntryState = MaterializerEntry.GetEntry(feedEntry); + MaterializerEntry feedEntryState = MaterializerEntry.GetEntry(feedEntry, materializerContext); if (!feedEntryState.EntityHasBeenResolved) { materializer.EntryValueMaterializationPolicy.Materialize(feedEntryState, nestedExpectedType, /* includeLinks */ false); @@ -1020,7 +1022,7 @@ private static void MaterializeToList( /// <param name="links">List to get value from.</param> /// <param name="propertyName">Property name to look up.</param> /// <returns>The specified property (never null).</returns> - private static MaterializerNavigationLink GetPropertyOrThrow(IEnumerable<ODataNestedResourceInfo> links, string propertyName) + private static MaterializerNavigationLink GetPropertyOrThrow(IEnumerable<ODataNestedResourceInfo> links, string propertyName, IODataMaterializerContext materializerContext) { ODataNestedResourceInfo link = null; if (links != null) @@ -1033,7 +1035,7 @@ private static MaterializerNavigationLink GetPropertyOrThrow(IEnumerable<ODataNe throw new InvalidOperationException(DSClient.Strings.AtomMaterializer_PropertyMissing(propertyName)); } - return MaterializerNavigationLink.GetLink(link); + return MaterializerNavigationLink.GetLink(link, materializerContext); } /// <summary>Merges a list into the property of a given <paramref name="entry"/>.</summary> diff --git a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializerInvoker.cs b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializerInvoker.cs index dc79df58ed..046ef198c7 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializerInvoker.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializerInvoker.cs @@ -59,11 +59,12 @@ internal static List<TTarget> ListAsElementType<T, TTarget>(object materializer, internal static bool ProjectionCheckValueForPathIsNull( object entry, Type expectedType, - object path) + object path, + IODataMaterializerContext materializerContext) { Debug.Assert(entry.GetType() == typeof(ODataResource), "entry.GetType() == typeof(ODataResource)"); Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)"); - return ODataEntityMaterializer.ProjectionCheckValueForPathIsNull(MaterializerEntry.GetEntry((ODataResource)entry), expectedType, (ProjectionPath)path); + return ODataEntityMaterializer.ProjectionCheckValueForPathIsNull(MaterializerEntry.GetEntry((ODataResource)entry, materializerContext), expectedType, (ProjectionPath)path, materializerContext); } /// <summary>Provides support for Select invocations for projections.</summary> @@ -85,17 +86,18 @@ internal static IEnumerable ProjectionSelect( Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())"); Debug.Assert(entry.GetType() == typeof(ODataResource), "entry.GetType() == typeof(ODataResource)"); Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)"); - return ODataEntityMaterializer.ProjectionSelect((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataResource)entry), expectedType, resultType, (ProjectionPath)path, selector); + ODataEntityMaterializer entityMaterializer = (ODataEntityMaterializer)materializer; + return ODataEntityMaterializer.ProjectionSelect(entityMaterializer, MaterializerEntry.GetEntry((ODataResource)entry, entityMaterializer.MaterializerContext), expectedType, resultType, (ProjectionPath)path, selector); } /// <summary>Provides support for getting payload entries during projections.</summary> /// <param name="entry">Entry to get sub-entry from.</param> /// <param name="name">Name of sub-entry.</param> /// <returns>The sub-entry (never null).</returns> - internal static object ProjectionGetEntry(object entry, string name) + internal static object ProjectionGetEntry(object entry, string name, IODataMaterializerContext materializerContext) { Debug.Assert(entry.GetType() == typeof(ODataResource), "entry.GetType() == typeof(ODataResource)"); - return ODataEntityMaterializer.ProjectionGetEntry(MaterializerEntry.GetEntry((ODataResource)entry), name); + return ODataEntityMaterializer.ProjectionGetEntry(MaterializerEntry.GetEntry((ODataResource)entry, materializerContext), name, materializerContext); } /// <summary>Initializes a projection-driven entry (with a specific type and specific properties).</summary> @@ -116,7 +118,8 @@ internal static object ProjectionInitializeEntity( { Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())"); Debug.Assert(entry.GetType() == typeof(ODataResource), "entry.GetType() == typeof(ODataResource)"); - return ODataEntityMaterializer.ProjectionInitializeEntity((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataResource)entry), expectedType, resultType, properties, propertyValues); + ODataEntityMaterializer entityMaterializer = (ODataEntityMaterializer)materializer; + return ODataEntityMaterializer.ProjectionInitializeEntity(entityMaterializer, MaterializerEntry.GetEntry((ODataResource)entry, entityMaterializer.MaterializerContext), expectedType, resultType, properties, propertyValues); } /// <summary>Projects a simple value from the specified <paramref name="path"/>.</summary> @@ -134,7 +137,8 @@ internal static object ProjectionValueForPath(object materializer, object entry, Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())"); Debug.Assert(entry.GetType() == typeof(ODataResource), "entry.GetType() == typeof(ODataResource)"); Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)"); - return ((ODataEntityMaterializer)materializer).ProjectionValueForPath(MaterializerEntry.GetEntry((ODataResource)entry), expectedType, (ProjectionPath)path); + ODataEntityMaterializer entityMaterializer = (ODataEntityMaterializer)materializer; + return entityMaterializer.ProjectionValueForPath(MaterializerEntry.GetEntry((ODataResource)entry, entityMaterializer.MaterializerContext), expectedType, (ProjectionPath)path); } /// <summary>Projects a simple dynamic value from the specified <paramref name="path"/>.</summary> @@ -152,7 +156,8 @@ internal static object ProjectionDynamicValueForPath(object materializer, object Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())"); Debug.Assert(entry.GetType() == typeof(ODataResource), "entry.GetType() == typeof(ODataResource)"); Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)"); - return ((ODataEntityMaterializer)materializer).ProjectionDynamicValueForPath(MaterializerEntry.GetEntry((ODataResource)entry), expectedPropertyType, (ProjectionPath)path); + ODataEntityMaterializer entityMaterializer = (ODataEntityMaterializer)materializer; + return entityMaterializer.ProjectionDynamicValueForPath(MaterializerEntry.GetEntry((ODataResource)entry, entityMaterializer.MaterializerContext), expectedPropertyType, (ProjectionPath)path); } /// <summary>Materializes an entry with no special selection.</summary> @@ -164,7 +169,8 @@ internal static object DirectMaterializePlan(object materializer, object entry, { Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())"); Debug.Assert(entry.GetType() == typeof(ODataResource), "entry.GetType() == typeof(ODataResource)"); - return ODataEntityMaterializer.DirectMaterializePlan((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataResource)entry), expectedEntryType); + ODataEntityMaterializer entityMaterializer = (ODataEntityMaterializer)materializer; + return ODataEntityMaterializer.DirectMaterializePlan(entityMaterializer, MaterializerEntry.GetEntry((ODataResource)entry, entityMaterializer.MaterializerContext), expectedEntryType); } /// <summary>Materializes an entry without including in-lined expanded links.</summary> @@ -176,7 +182,8 @@ internal static object ShallowMaterializePlan(object materializer, object entry, { Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())"); Debug.Assert(entry.GetType() == typeof(ODataResource), "entry.GetType() == typeof(ODataResource)"); - return ODataEntityMaterializer.ShallowMaterializePlan((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataResource)entry), expectedEntryType); + ODataEntityMaterializer entityMaterializer = (ODataEntityMaterializer)materializer; + return ODataEntityMaterializer.ShallowMaterializePlan(entityMaterializer, MaterializerEntry.GetEntry((ODataResource)entry, entityMaterializer.MaterializerContext), expectedEntryType); } } } diff --git a/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs b/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs index e02c408524..713b2673e7 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs @@ -19,10 +19,10 @@ internal static class ODataItemExtensions /// </summary> /// <param name="property">The property.</param> /// <returns>The materialized value.</returns> - public static object GetMaterializedValue(this ODataProperty property) + public static object GetMaterializedValue(this ODataProperty property, IODataMaterializerContext materializerContext) { ODataAnnotatable annotatableObject = property.Value as ODataAnnotatable ?? property; - return GetMaterializedValueCore(annotatableObject); + return GetMaterializedValueCore(annotatableObject, materializerContext); } /// <summary> @@ -30,10 +30,10 @@ public static object GetMaterializedValue(this ODataProperty property) /// </summary> /// <param name="property">The property.</param> /// <returns><c>true</c> if the value has been materialized; otherwise, <c>false</c>.</returns> - public static bool HasMaterializedValue(this ODataProperty property) + public static bool HasMaterializedValue(this ODataProperty property, IODataMaterializerContext materializerContext) { ODataAnnotatable annotatableObject = property.Value as ODataAnnotatable ?? property; - return HasMaterializedValueCore(annotatableObject); + return HasMaterializedValueCore(annotatableObject, materializerContext); } /// <summary> @@ -41,10 +41,10 @@ public static bool HasMaterializedValue(this ODataProperty property) /// </summary> /// <param name="property">The property.</param> /// <param name="materializedValue">The materialized value.</param> - public static void SetMaterializedValue(this ODataProperty property, object materializedValue) + public static void SetMaterializedValue(this ODataProperty property, object materializedValue, IODataMaterializerContext materializerContext) { ODataAnnotatable annotatableObject = property.Value as ODataAnnotatable ?? property; - SetMaterializedValueCore(annotatableObject, materializedValue); + SetMaterializedValueCore(annotatableObject, materializedValue, materializerContext); } /// <summary> @@ -52,9 +52,9 @@ public static void SetMaterializedValue(this ODataProperty property, object mate /// </summary> /// <param name="annotatableObject">The annotatable object.</param> /// <returns>The materialized value</returns> - private static object GetMaterializedValueCore(ODataAnnotatable annotatableObject) + private static object GetMaterializedValueCore(ODataAnnotatable annotatableObject, IODataMaterializerContext materializerContext) { - MaterializerPropertyValue value = annotatableObject.GetAnnotation<MaterializerPropertyValue>(); + MaterializerPropertyValue value = materializerContext.GetAnnotation<MaterializerPropertyValue>(annotatableObject); Debug.Assert(value != null, "MaterializedValue not set"); return value.Value; } @@ -64,9 +64,9 @@ private static object GetMaterializedValueCore(ODataAnnotatable annotatableObjec /// </summary> /// <param name="annotatableObject">The annotatable object.</param> /// <returns><c>true</c> if the value has been materialized; otherwise, <c>false</c>.</returns> - private static bool HasMaterializedValueCore(ODataAnnotatable annotatableObject) + private static bool HasMaterializedValueCore(ODataAnnotatable annotatableObject, IODataMaterializerContext materializerContext) { - return annotatableObject.GetAnnotation<MaterializerPropertyValue>() != null; + return materializerContext.GetAnnotation<MaterializerPropertyValue>(annotatableObject) != null; } /// <summary> @@ -74,10 +74,10 @@ private static bool HasMaterializedValueCore(ODataAnnotatable annotatableObject) /// </summary> /// <param name="annotatableObject">The annotatable object.</param> /// <param name="materializedValue">The materialized value.</param> - private static void SetMaterializedValueCore(ODataAnnotatable annotatableObject, object materializedValue) + private static void SetMaterializedValueCore(ODataAnnotatable annotatableObject, object materializedValue, IODataMaterializerContext materializerContext) { MaterializerPropertyValue materializerValue = new MaterializerPropertyValue { Value = materializedValue }; - annotatableObject.SetAnnotation(materializerValue); + materializerContext.SetAnnotation(annotatableObject, materializerValue); } /// <summary> diff --git a/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs index 950f9c6c23..f065943b06 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs @@ -232,7 +232,7 @@ public static ODataMaterializer CreateMaterializerForMessage( } ODataReaderWrapper reader = ODataReaderWrapper.Create(messageReader, payloadKind, edmType, responseInfo.ResponsePipeline); - EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context); + EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context, materializerContext); LoadPropertyResponseInfo loadPropertyResponseInfo = responseInfo as LoadPropertyResponseInfo; if (loadPropertyResponseInfo != null) diff --git a/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs b/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs index f885159b80..946eb001bd 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs @@ -22,6 +22,7 @@ internal class ODataMaterializerContext : IODataMaterializerContext internal ODataMaterializerContext(ResponseInfo responseInfo) { this.ResponseInfo = responseInfo; + this.AnnotationsCache = new MaterializerAnnotationsCache(); } /// <summary> @@ -84,5 +85,7 @@ public IEdmType ResolveExpectedTypeForReading(Type expectedType) { return this.ResponseInfo.TypeResolver.ResolveExpectedTypeForReading(expectedType); } + + public MaterializerAnnotationsCache AnnotationsCache { get; private set; } } } diff --git a/src/Microsoft.OData.Client/Materialization/ODataReaderEntityMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataReaderEntityMaterializer.cs index 6553d4ab6f..f474624db7 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataReaderEntityMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataReaderEntityMaterializer.cs @@ -44,7 +44,7 @@ public ODataReaderEntityMaterializer( : base(materializerContext, entityTrackingAdapter, queryComponents, expectedType, materializeEntryPlan) { this.messageReader = odataMessageReader; - this.feedEntryAdapter = new FeedAndEntryMaterializerAdapter(odataMessageReader, reader, materializerContext.Model, entityTrackingAdapter.MergeOption); + this.feedEntryAdapter = new FeedAndEntryMaterializerAdapter(odataMessageReader, reader, materializerContext.Model, entityTrackingAdapter.MergeOption, materializerContext); } /// <summary> @@ -116,7 +116,7 @@ protected override ODataFormat Format /// <param name="responseInfo">The current ResponseInfo object</param> /// <param name="expectedType">The expected type</param> /// <returns>the MaterializerEntry that was read</returns> - internal static MaterializerEntry ParseSingleEntityPayload(IODataResponseMessage message, ResponseInfo responseInfo, Type expectedType) + internal static MaterializerEntry ParseSingleEntityPayload(IODataResponseMessage message, ResponseInfo responseInfo, Type expectedType, IODataMaterializerContext materializerContext) { ODataPayloadKind messageType = ODataPayloadKind.Resource; using (ODataMessageReader messageReader = CreateODataMessageReader(message, responseInfo, ref messageType)) @@ -124,7 +124,7 @@ internal static MaterializerEntry ParseSingleEntityPayload(IODataResponseMessage IEdmType edmType = responseInfo.TypeResolver.ResolveExpectedTypeForReading(expectedType); ODataReaderWrapper reader = ODataReaderWrapper.Create(messageReader, messageType, edmType, responseInfo.ResponsePipeline); - FeedAndEntryMaterializerAdapter parser = new FeedAndEntryMaterializerAdapter(messageReader, reader, responseInfo.Model, responseInfo.MergeOption); + FeedAndEntryMaterializerAdapter parser = new FeedAndEntryMaterializerAdapter(messageReader, reader, responseInfo.Model, responseInfo.MergeOption, materializerContext); ODataResource entry = null; bool readFeed = false; @@ -154,7 +154,7 @@ internal static MaterializerEntry ParseSingleEntityPayload(IODataResponseMessage } } - return MaterializerEntry.GetEntry(entry); + return MaterializerEntry.GetEntry(entry, materializerContext); } } diff --git a/src/Microsoft.OData.Client/Materialization/StructuralValueMaterializationPolicy.cs b/src/Microsoft.OData.Client/Materialization/StructuralValueMaterializationPolicy.cs index c34e4423f4..fe61904f9d 100644 --- a/src/Microsoft.OData.Client/Materialization/StructuralValueMaterializationPolicy.cs +++ b/src/Microsoft.OData.Client/Materialization/StructuralValueMaterializationPolicy.cs @@ -114,7 +114,7 @@ internal void MaterializePrimitiveDataValue(Type type, ODataProperty property) Debug.Assert(type != null, "type != null"); Debug.Assert(property != null, "atomProperty != null"); - if (!property.HasMaterializedValue()) + if (!property.HasMaterializedValue(this.MaterializerContext)) { object value = property.Value; ODataUntypedValue untypedVal = value as ODataUntypedValue; @@ -125,7 +125,7 @@ internal void MaterializePrimitiveDataValue(Type type, ODataProperty property) } object materializedValue = this.PrimitivePropertyConverter.ConvertPrimitiveValue(value, type); - property.SetMaterializedValue(materializedValue); + property.SetMaterializedValue(materializedValue, this.MaterializerContext); } } @@ -218,7 +218,7 @@ internal void ApplyDataValue(ClientTypeAnnotation type, ODataProperty property, else { this.MaterializePrimitiveDataValue(prop.NullablePropertyType, property); - prop.SetValue(instance, property.GetMaterializedValue(), property.Name, true /* allowAdd? */); + prop.SetValue(instance, property.GetMaterializedValue(this.MaterializerContext), property.Name, true /* allowAdd? */); } if (!this.MaterializerContext.Context.DisableInstanceAnnotationMaterialization) diff --git a/src/Microsoft.OData.Client/MaterializeFromAtom.cs b/src/Microsoft.OData.Client/MaterializeFromAtom.cs index f2f1525ab0..23621de875 100644 --- a/src/Microsoft.OData.Client/MaterializeFromAtom.cs +++ b/src/Microsoft.OData.Client/MaterializeFromAtom.cs @@ -111,7 +111,7 @@ internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> e Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType); QueryComponents qc = new QueryComponents(null, Util.ODataVersionEmpty, elementType, null, null); ODataMaterializerContext context = new ODataMaterializerContext(responseInfo); - EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context); + EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context, context); this.materializer = new ODataEntriesEntityMaterializer(entries, context, entityTrackingAdapter, qc, materializerType, null, format); } diff --git a/src/Microsoft.OData.Client/ODataAnnotatableExtensions.cs b/src/Microsoft.OData.Client/ODataAnnotatableExtensions.cs index 7869da711b..eacb02dc99 100644 --- a/src/Microsoft.OData.Client/ODataAnnotatableExtensions.cs +++ b/src/Microsoft.OData.Client/ODataAnnotatableExtensions.cs @@ -11,22 +11,22 @@ namespace Microsoft.OData.Client { internal static class ODataAnnotatableExtensions { - public static void SetAnnotation<T>(this ODataAnnotatable annotatable, T annotation) - where T : class - { - Debug.Assert(annotatable != null, "annotatable != null"); - Debug.Assert(annotation != null, "annotation != null"); - - InternalDictionary<T>.SetAnnotation(annotatable, annotation); - } - - public static T GetAnnotation<T>(this ODataAnnotatable annotatable) - where T : class - { - Debug.Assert(annotatable != null, "annotatable != null"); - - return InternalDictionary<T>.GetAnnotation(annotatable); - } + //public static void SetAnnotation<T>(this ODataAnnotatable annotatable, T annotation) + // where T : class + //{ + // Debug.Assert(annotatable != null, "annotatable != null"); + // Debug.Assert(annotation != null, "annotation != null"); + + // InternalDictionary<T>.SetAnnotation(annotatable, annotation); + //} + + //public static T GetAnnotation<T>(this ODataAnnotatable annotatable) + // where T : class + //{ + // Debug.Assert(annotatable != null, "annotatable != null"); + + // return InternalDictionary<T>.GetAnnotation(annotatable); + //} private static class InternalDictionary<T> where T : class { diff --git a/src/Microsoft.OData.Client/SaveResult.cs b/src/Microsoft.OData.Client/SaveResult.cs index 38e1aa6fe2..1e0c20ef9f 100644 --- a/src/Microsoft.OData.Client/SaveResult.cs +++ b/src/Microsoft.OData.Client/SaveResult.cs @@ -865,7 +865,8 @@ private void HandleOperationResponseData(IODataResponseMessage responseMsg, Stre responseMsg.StatusCode, () => responseStream); - entry = ODataReaderEntityMaterializer.ParseSingleEntityPayload(responseMessageWrapper, responseInfo, entityDescriptor.Entity.GetType()); + ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo); + entry = ODataReaderEntityMaterializer.ParseSingleEntityPayload(responseMessageWrapper, responseInfo, entityDescriptor.Entity.GetType(), materializerContext); entityDescriptor.TransientEntityDescriptor = entry.EntityDescriptor; } catch (Exception ex) From fa9f65109c13e4b7d25c8f348d5153e52b9ed827 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Mon, 12 Sep 2022 08:32:14 +0300 Subject: [PATCH 02/16] Move up the scope of annotations cache to request-level instead of response-level --- src/Microsoft.OData.Client/BaseAsyncResult.cs | 3 +++ src/Microsoft.OData.Client/BaseSaveResult.cs | 3 +++ src/Microsoft.OData.Client/BatchSaveResult.cs | 6 ++++-- src/Microsoft.OData.Client/DataServiceRequest.cs | 7 +++++-- .../Materialization/ODataMaterializer.cs | 5 +++-- .../Materialization/ODataMaterializerContext.cs | 4 ++-- src/Microsoft.OData.Client/MaterializeFromAtom.cs | 9 +++++---- src/Microsoft.OData.Client/QueryResult.cs | 3 ++- src/Microsoft.OData.Client/SaveResult.cs | 4 ++-- 9 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.OData.Client/BaseAsyncResult.cs b/src/Microsoft.OData.Client/BaseAsyncResult.cs index 7b3d498484..aa31139238 100644 --- a/src/Microsoft.OData.Client/BaseAsyncResult.cs +++ b/src/Microsoft.OData.Client/BaseAsyncResult.cs @@ -11,6 +11,7 @@ namespace Microsoft.OData.Client using System.IO; using System.Threading; using Microsoft.OData; + using Microsoft.OData.Client.Materialization; /// <summary> /// Implementation of IAsyncResult @@ -26,6 +27,8 @@ internal abstract class BaseAsyncResult : IAsyncResult /// <summary>wrapped request</summary> protected PerRequest perRequest; + protected MaterializerAnnotationsCache annotationsCache = new MaterializerAnnotationsCache(); + /// <summary> /// The int equivalent for true. /// </summary> diff --git a/src/Microsoft.OData.Client/BaseSaveResult.cs b/src/Microsoft.OData.Client/BaseSaveResult.cs index 247c8c163b..9f8f46406e 100644 --- a/src/Microsoft.OData.Client/BaseSaveResult.cs +++ b/src/Microsoft.OData.Client/BaseSaveResult.cs @@ -19,6 +19,7 @@ namespace Microsoft.OData.Client using System.Text; using System.Threading; using Microsoft.OData; + using Microsoft.OData.Client.Materialization; using Microsoft.OData.Client.Metadata; #endregion Namespaces @@ -66,6 +67,8 @@ internal abstract class BaseSaveResult : BaseAsyncResult /// <summary>application/json Content-Type header</summary> private const string JsonContentTypeHeader = "application/json"; + protected MaterializerAnnotationsCache annotationsCache = new MaterializerAnnotationsCache(); + #endregion Private Fields /// <summary> diff --git a/src/Microsoft.OData.Client/BatchSaveResult.cs b/src/Microsoft.OData.Client/BatchSaveResult.cs index f5fe794c94..6327a2b612 100644 --- a/src/Microsoft.OData.Client/BatchSaveResult.cs +++ b/src/Microsoft.OData.Client/BatchSaveResult.cs @@ -253,7 +253,8 @@ protected override MaterializeAtom GetMaterializer(EntityDescriptor entityDescri queryComponents, /*projectionPlan*/ null, this.currentOperationResponse.CreateResponseMessage(), - ODataPayloadKind.Resource); + ODataPayloadKind.Resource, + this.annotationsCache); } /// <summary> @@ -688,7 +689,8 @@ private IEnumerable<OperationResponse> HandleBatchResponse(ODataBatchReader batc null, this.currentOperationResponse.Headers.GetHeader(XmlConstants.HttpContentType), this.currentOperationResponse.CreateResponseMessage(), - query.PayloadKind); + query.PayloadKind, + this.annotationsCache); qresponse = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, materializer); } } diff --git a/src/Microsoft.OData.Client/DataServiceRequest.cs b/src/Microsoft.OData.Client/DataServiceRequest.cs index 6788ebc089..0b6c120703 100644 --- a/src/Microsoft.OData.Client/DataServiceRequest.cs +++ b/src/Microsoft.OData.Client/DataServiceRequest.cs @@ -12,6 +12,7 @@ namespace Microsoft.OData.Client using System.Linq; using System.Net; using Microsoft.OData; + using Microsoft.OData.Client.Materialization; /// <summary>Non-generic placeholder for generic implementation</summary> public abstract class DataServiceRequest @@ -66,7 +67,8 @@ internal static MaterializeAtom Materialize( ProjectionPlan plan, string contentType, IODataResponseMessage message, - ODataPayloadKind expectedPayloadKind) + ODataPayloadKind expectedPayloadKind, + MaterializerAnnotationsCache annotationsCache) { Debug.Assert(queryComponents != null, "querycomponents"); Debug.Assert(message != null, "message"); @@ -77,7 +79,8 @@ internal static MaterializeAtom Materialize( return MaterializeAtom.EmptyResults; } - return new MaterializeAtom(responseInfo, queryComponents, plan, message, expectedPayloadKind); + + return new MaterializeAtom(responseInfo, queryComponents, plan, message, expectedPayloadKind, annotationsCache); } /// <summary> diff --git a/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs index f065943b06..91f2f98d12 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs @@ -191,7 +191,8 @@ public static ODataMaterializer CreateMaterializerForMessage( Type materializerType, QueryComponents queryComponents, ProjectionPlan plan, - ODataPayloadKind payloadKind) + ODataPayloadKind payloadKind, + MaterializerAnnotationsCache annotationsCache) { ODataMessageReader messageReader = CreateODataMessageReader(responseMessage, responseInfo, ref payloadKind); @@ -200,7 +201,7 @@ public static ODataMaterializer CreateMaterializerForMessage( try { - ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo); + ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, annotationsCache); // Since in V1/V2, astoria client allowed Execute<object> and depended on the typeresolver or the wire type name // to get the clr type to materialize. Hence if we see the materializer type as object, we should set the edmtype diff --git a/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs b/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs index 946eb001bd..923a221257 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs @@ -19,10 +19,10 @@ internal class ODataMaterializerContext : IODataMaterializerContext /// Initializes a materializer context /// </summary> /// <param name="responseInfo">Response information used to initialize with the materializer</param> - internal ODataMaterializerContext(ResponseInfo responseInfo) + internal ODataMaterializerContext(ResponseInfo responseInfo, MaterializerAnnotationsCache annotationsCache) { this.ResponseInfo = responseInfo; - this.AnnotationsCache = new MaterializerAnnotationsCache(); + this.AnnotationsCache = annotationsCache; } /// <summary> diff --git a/src/Microsoft.OData.Client/MaterializeFromAtom.cs b/src/Microsoft.OData.Client/MaterializeFromAtom.cs index 23621de875..401a4e0a5b 100644 --- a/src/Microsoft.OData.Client/MaterializeFromAtom.cs +++ b/src/Microsoft.OData.Client/MaterializeFromAtom.cs @@ -79,7 +79,8 @@ internal MaterializeAtom( QueryComponents queryComponents, ProjectionPlan plan, IODataResponseMessage responseMessage, - ODataPayloadKind payloadKind) + ODataPayloadKind payloadKind, + MaterializerAnnotationsCache annotationsCache) { Debug.Assert(queryComponents != null, "queryComponents != null"); @@ -91,7 +92,7 @@ internal MaterializeAtom( Type implementationType; Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType); - this.materializer = ODataMaterializer.CreateMaterializerForMessage(responseMessage, responseInfo, materializerType, queryComponents, plan, payloadKind); + this.materializer = ODataMaterializer.CreateMaterializerForMessage(responseMessage, responseInfo, materializerType, queryComponents, plan, payloadKind, annotationsCache); } /// <summary> @@ -101,7 +102,7 @@ internal MaterializeAtom( /// <param name="entries">entries that needs to be materialized.</param> /// <param name="elementType">result type.</param> /// <param name="format">The format of the response being materialized from.</param> - internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> entries, Type elementType, ODataFormat format) + internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> entries, Type elementType, ODataFormat format, MaterializerAnnotationsCache annotationsCache) { this.responseInfo = responseInfo; this.elementType = elementType; @@ -110,7 +111,7 @@ internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> e Type implementationType; Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType); QueryComponents qc = new QueryComponents(null, Util.ODataVersionEmpty, elementType, null, null); - ODataMaterializerContext context = new ODataMaterializerContext(responseInfo); + ODataMaterializerContext context = new ODataMaterializerContext(responseInfo, annotationsCache); EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context, context); this.materializer = new ODataEntriesEntityMaterializer(entries, context, entityTrackingAdapter, qc, materializerType, null, format); } diff --git a/src/Microsoft.OData.Client/QueryResult.cs b/src/Microsoft.OData.Client/QueryResult.cs index 228ac6d4a5..812da403b0 100644 --- a/src/Microsoft.OData.Client/QueryResult.cs +++ b/src/Microsoft.OData.Client/QueryResult.cs @@ -707,7 +707,8 @@ private MaterializeAtom CreateMaterializer(ProjectionPlan plan, ODataPayloadKind plan, this.ContentType, responseMessageWrapper, - payloadKind); + payloadKind, + this.annotationsCache); } } } diff --git a/src/Microsoft.OData.Client/SaveResult.cs b/src/Microsoft.OData.Client/SaveResult.cs index 1e0c20ef9f..80ce34218f 100644 --- a/src/Microsoft.OData.Client/SaveResult.cs +++ b/src/Microsoft.OData.Client/SaveResult.cs @@ -360,7 +360,7 @@ protected override MaterializeAtom GetMaterializer(EntityDescriptor entityDescri { Debug.Assert(this.cachedResponse.Exception == null && this.cachedResponse.MaterializerEntry != null, "this.cachedResponse.Exception == null && this.cachedResponse.Entry != null"); ODataResource entry = this.cachedResponse.MaterializerEntry == null ? null : this.cachedResponse.MaterializerEntry.Entry; - return new MaterializeAtom(responseInfo, new[] { entry }, entityDescriptor.Entity.GetType(), this.cachedResponse.MaterializerEntry.Format); + return new MaterializeAtom(responseInfo, new[] { entry }, entityDescriptor.Entity.GetType(), this.cachedResponse.MaterializerEntry.Format, annotationsCache); } /// <summary> @@ -865,7 +865,7 @@ private void HandleOperationResponseData(IODataResponseMessage responseMsg, Stre responseMsg.StatusCode, () => responseStream); - ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo); + ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, this.annotationsCache); entry = ODataReaderEntityMaterializer.ParseSingleEntityPayload(responseMessageWrapper, responseInfo, entityDescriptor.Entity.GetType(), materializerContext); entityDescriptor.TransientEntityDescriptor = entry.EntityDescriptor; } From 17ff489a14a9f919f2dc47cfb11e04ca64c3a36e Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Mon, 12 Sep 2022 10:25:13 +0300 Subject: [PATCH 03/16] Fix e2e tests --- .../Tests/DataServiceRequestTests.cs | 5 ++- ...llectionValueMaterializationPolicyTests.cs | 10 ++--- ...ializationPolicyForComplexResourceTests.cs | 44 ++++++++++--------- ...ntryValueMaterializationPolicyUnitTests.cs | 5 ++- ...eedAndEntryMaterializerAdapterUnitTests.cs | 3 +- .../Materialization/MaterializerEntryTests.cs | 4 +- .../ODataEntityMaterializerUnitTests.cs | 11 +++-- ...ODataEntriesEntityMaterializerUnitTests.cs | 7 +-- ...rimitiveValueMaterializationPolicyTests.cs | 2 +- .../TestMaterializerContext.cs | 5 ++- .../Tests/T4/ODataT4CamelCaseTests.cs | 25 ++++++----- 11 files changed, 70 insertions(+), 51 deletions(-) diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/DataServiceRequestTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/DataServiceRequestTests.cs index 35813b16bb..07fd87e2ee 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/DataServiceRequestTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/DataServiceRequestTests.cs @@ -12,6 +12,7 @@ namespace AstoriaUnitTests.TDD.Tests.Client using System.Net; using Microsoft.OData; using Microsoft.OData.Client; + using Microsoft.OData.Client.Materialization; using Microsoft.OData.Client.TDDUnitTests; using Xunit; @@ -58,13 +59,15 @@ private void MaterializeTest(HttpStatusCode statusCode, ODataPayloadKind payload new HeaderCollection(), (int)statusCode, () => new MemoryStream()); + var annotationsCache = new MaterializerAnnotationsCache(); var materialize = DataServiceRequest.Materialize( responseInfo, queryComponents, null, "application/json", responseMessage, - payloadKind); + payloadKind, + annotationsCache); Assert.Null(materialize.Context); Assert.Null(materialize.Current); var enumerable = materialize.Cast<object>(); diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/CollectionValueMaterializationPolicyTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/CollectionValueMaterializationPolicyTests.cs index ec244d9fad..e0e3c7fdb9 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/CollectionValueMaterializationPolicyTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/CollectionValueMaterializationPolicyTests.cs @@ -130,7 +130,7 @@ public void AddingCollectionToComplexCollectionShouldFail() [Fact] public void DataServicCollectionOfTAsCollectionTypeShouldFailForPrimitiveOrComplexCollections() { - var testContext = new TestMaterializerContext(); + var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); var edmType = testContext.Model.GetOrCreateEdmType(typeof(MyInfo)); var clientTypeAnnotation = new ClientTypeAnnotation(edmType, typeof(MyInfo), "MyInfo", testContext.Model); @@ -141,7 +141,7 @@ public void DataServicCollectionOfTAsCollectionTypeShouldFailForPrimitiveOrCompl [Fact] public void CreateCollectionInstanceShouldFailOnTypeWithNoParametersLessConstructors() { - var testContext = new TestMaterializerContext(); + var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); var edmType = testContext.Model.GetOrCreateEdmType(typeof(ListWithNoEmptyConstructors)); var clientTypeAnnotation = new ClientTypeAnnotation(edmType, typeof(ListWithNoEmptyConstructors), "Points", testContext.Model); @@ -153,7 +153,7 @@ public void CreateCollectionInstanceShouldFailOnTypeWithNoParametersLessConstruc public void CreateCollectionPropertyInstanceShouldFailOnTypeWithNoParametersLessConstructors() { var odataProperty = new ODataProperty() { Name = "foo", Value = new ODataCollectionValue() { TypeName = "Points" } }; - var testContext = new TestMaterializerContext(); + var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); testContext.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { var edmType = testContext.Model.GetOrCreateEdmType(typeof(ListWithNoEmptyConstructors)); @@ -167,7 +167,7 @@ public void CreateCollectionPropertyInstanceShouldFailOnTypeWithNoParametersLess [Fact] public void NonMissingMethodExceptionOnCreateInstanceShouldNotBeCaught() { - var testContext = new TestMaterializerContext(); + var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); var edmType = testContext.Model.GetOrCreateEdmType(typeof(ListWithNoEmptyConstructors)); var clientTypeAnnotation = new ClientTypeAnnotation(edmType, typeof(ErrorThrowingList), "Points", testContext.Model); @@ -181,7 +181,7 @@ public void NonMissingMethodExceptionOnCreateInstanceShouldNotBeCaught() internal CollectionValueMaterializationPolicy CreateCollectionValueMaterializationPolicy() { - return CreateCollectionValueMaterializationPolicy(new TestMaterializerContext()); + return CreateCollectionValueMaterializationPolicy(new TestMaterializerContext(new MaterializerAnnotationsCache())); } internal CollectionValueMaterializationPolicy CreateCollectionValueMaterializationPolicy(IODataMaterializerContext materializerContext) diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs index 32ca9ea419..8652bd87a5 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs @@ -50,7 +50,7 @@ public void ComplexWithPrimitiveValueShouldMaterialize() [Fact] public void ApplyNonExistantPropertyWithIgnoreMissingPropertiesShouldNotError() { - TestMaterializerContext context = new TestMaterializerContext() { UndeclaredPropertyBehavior = DSClient.UndeclaredPropertyBehavior.Support }; + TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()) { UndeclaredPropertyBehavior = DSClient.UndeclaredPropertyBehavior.Support }; CollectionValueMaterializationPolicyTests.Point point = new CollectionValueMaterializationPolicyTests.Point(); ODataProperty property = new ODataProperty() { Name = "Z", Value = 10 }; this.CreateEntryMaterializationPolicy(context) @@ -60,7 +60,7 @@ public void ApplyNonExistantPropertyWithIgnoreMissingPropertiesShouldNotError() [Fact] public void ApplyNullOnCollectionPropertyShouldError() { - TestMaterializerContext context = new TestMaterializerContext(); + TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); ODataProperty property = new ODataProperty() { Name = "Strings", Value = null }; @@ -71,7 +71,7 @@ public void ApplyNullOnCollectionPropertyShouldError() [Fact] public void ApplyStringValueForCollectionPropertyShouldError() { - TestMaterializerContext context = new TestMaterializerContext(); + TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); ODataProperty property = new ODataProperty() { Name = "Strings", Value = "foo" }; @@ -82,7 +82,8 @@ public void ApplyStringValueForCollectionPropertyShouldError() [Fact] public void MaterializeDerivedComplexForBaseComplexTypeProperty() { - TestMaterializerContext context = new TestMaterializerContext(); + var annotationsCache = new MaterializerAnnotationsCache(); + TestMaterializerContext context = new TestMaterializerContext(annotationsCache); //In a true client, a TypeResolver will be used to resolve derived property type. context.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => @@ -98,7 +99,7 @@ public void MaterializeDerivedComplexForBaseComplexTypeProperty() }; var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); - var materializerEntry = MaterializerEntry.CreateEntry(derivedResource, ODataFormat.Json, true, clientEdmModel); + var materializerEntry = MaterializerEntry.CreateEntry(derivedResource, ODataFormat.Json, true, clientEdmModel, context); this.CreateEntryMaterializationPolicy(context).Materialize(materializerEntry, typeof(ChildComplexType), false); var derived = materializerEntry.ResolvedObject as DerivedComplexType; @@ -109,7 +110,7 @@ public void MaterializeDerivedComplexForBaseComplexTypeProperty() [Fact] public void ApplyDerivedComplexForBaseComplexTypeProperty() { - TestMaterializerContext context = new TestMaterializerContext(); + TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); context.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { @@ -140,7 +141,7 @@ public void ApplyDerivedComplexForBaseComplexTypeProperty() [Fact] public void ApplyODataCollectionValueToNonNullExistingCollectionProperty() { - TestMaterializerContext context = new TestMaterializerContext(); + TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); complexInstance.Strings.Add("ShouldBeCleared"); @@ -156,7 +157,7 @@ public void ApplyODataCollectionValueToNonNullExistingCollectionProperty() [Fact] public void ApplyODataCollectionValueToNullCollectionProperty() { - TestMaterializerContext context = new TestMaterializerContext(); + TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); complexInstance.Strings = null; ODataProperty property = new ODataProperty() { Name = "Strings", Value = new ODataCollectionValue() { Items = new string[] { "foo" }, TypeName = typeof(ComplexTypeWithPrimitiveCollection).FullName } }; @@ -171,7 +172,7 @@ public void ValueShouldBeAppliedRegardlessIfPropertyStartsNullOrNot() { foreach (var startingPropertyState in new ChildComplexType[] { null, new ChildComplexType() }) { - TestMaterializerContext context = new TestMaterializerContext(); + TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithChildComplexType complexInstance = new ComplexTypeWithChildComplexType(); complexInstance.InnerComplexProperty = startingPropertyState; var innerEntry = new ODataResource() { Properties = new ODataProperty[] { new ODataProperty() { Name = "Prop", Value = 1 } } }; @@ -183,7 +184,7 @@ public void ValueShouldBeAppliedRegardlessIfPropertyStartsNullOrNot() [Fact] public void NullValueShouldBeAppliedToSubComplexValueProperty() { - TestMaterializerContext context = new TestMaterializerContext(); + TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithChildComplexType complexInstance = new ComplexTypeWithChildComplexType(); complexInstance.InnerComplexProperty = new ChildComplexType(); @@ -193,25 +194,25 @@ public void NullValueShouldBeAppliedToSubComplexValueProperty() private void ApplyInnerProperty(ODataResource innerResource, ComplexTypeWithChildComplexType parentInstance, TestMaterializerContext context = null) { - context = context ?? new TestMaterializerContext(); + context = context ?? new TestMaterializerContext(new MaterializerAnnotationsCache()); var resource = new ODataResource() { TypeName = "ComplexTypeWithChildComplexType", Properties = new ODataProperty[0] }; var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); - var materializerEntry = MaterializerEntry.CreateEntry(resource, ODataFormat.Json, false, clientEdmModel); + var materializerEntry = MaterializerEntry.CreateEntry(resource, ODataFormat.Json, false, clientEdmModel, context); materializerEntry.ResolvedObject = parentInstance; ODataNestedResourceInfo innerComplexP = new ODataNestedResourceInfo() { Name = "InnerComplexProperty", IsCollection = false }; MaterializerEntry innerMaterializerEntry; if (innerResource != null) { - innerMaterializerEntry = MaterializerEntry.CreateEntry(innerResource, ODataFormat.Json, true, clientEdmModel); + innerMaterializerEntry = MaterializerEntry.CreateEntry(innerResource, ODataFormat.Json, true, clientEdmModel, context); } else { innerMaterializerEntry = MaterializerEntry.CreateEmpty(); } - MaterializerNavigationLink.CreateLink(innerComplexP, innerMaterializerEntry); + MaterializerNavigationLink.CreateLink(innerComplexP, innerMaterializerEntry, context); materializerEntry.AddNestedResourceInfo(innerComplexP); var policy = this.CreateEntryMaterializationPolicy(context); @@ -223,8 +224,8 @@ internal EntryValueMaterializationPolicy CreateEntryMaterializationPolicy(TestMa { var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); - materializerContext = materializerContext ?? new TestMaterializerContext() { Model = clientEdmModel, Context = context }; - var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context); + materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context, materializerContext); var lazyPrimitivePropertyConverter = new DSClient.SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter()); var primitiveValueMaterializerPolicy = new PrimitiveValueMaterializationPolicy(materializerContext, lazyPrimitivePropertyConverter); var entryPolicy = new EntryValueMaterializationPolicy(materializerContext, adapter, lazyPrimitivePropertyConverter, null); @@ -273,7 +274,7 @@ public void ShouldMaterializeConcreteComplexCollectionDeclaredAsAbstract() new ODataResource(){Properties = new ODataProperty[]{ new ODataProperty(){Name="Points", Value = 0}, new ODataProperty(){Name="Diameter", Value = 18} }}, }); - var testContext = new TestMaterializerContext(); + var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); testContext.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { var edmType = testContext.Model.GetOrCreateEdmType(typeof(CollectionValueMaterializationPolicyTests.Circle)); @@ -327,9 +328,10 @@ internal ODataEntriesEntityMaterializer CreateODataEntriesEntityMaterializer( { var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); + materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; var resourceSet = new ODataResourceSet(); - MaterializerFeed.CreateFeed(resourceSet, resources); + MaterializerFeed.CreateFeed(resourceSet, resources, materializerContext); resources.ForEach(r => { if (r == null) @@ -338,11 +340,11 @@ internal ODataEntriesEntityMaterializer CreateODataEntriesEntityMaterializer( } else { - MaterializerEntry.CreateEntry(r, ODataFormat.Json, true, clientEdmModel); + MaterializerEntry.CreateEntry(r, ODataFormat.Json, true, clientEdmModel, materializerContext); } }); - materializerContext = materializerContext ?? new TestMaterializerContext() { Model = clientEdmModel, Context = context }; - var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context); + + var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context, materializerContext); QueryComponents components = new QueryComponents(new Uri("http://foo.com/Service"), new Version(4, 0), resourceType, null, new Dictionary<Expression, Expression>()); return new ODataEntriesEntityMaterializer(resources, materializerContext, adapter, components, resourceType, null, ODataFormat.Json); diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryValueMaterializationPolicyUnitTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryValueMaterializationPolicyUnitTests.cs index b11e9304b5..fa5ae38c07 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryValueMaterializationPolicyUnitTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryValueMaterializationPolicyUnitTests.cs @@ -41,7 +41,7 @@ public EntryValueMaterializationPolicyUnitTests() this.clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); this.clientEdmModel.GetOrCreateEdmType(typeof(TestCustomer)); this.clientEdmModel.GetOrCreateEdmType(typeof(TestOrder)); - this.materializerContext = new TestMaterializerContext() { Model = this.clientEdmModel }; + this.materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = this.clientEdmModel }; this.ordersProperty = this.clientEdmModel.GetClientTypeAnnotation(typeof(TestCustomer)).GetProperty("Orders", UndeclaredPropertyBehavior.ThrowException); } @@ -293,7 +293,8 @@ private void TestApplyItemsToCollection( entityTracker, option, clientEdmModel, - new DataServiceContext()); + new DataServiceContext(), + materializerContext); EntryValueMaterializationPolicy evmp = new EntryValueMaterializationPolicy( materializerContext, diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/FeedAndEntryMaterializerAdapterUnitTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/FeedAndEntryMaterializerAdapterUnitTests.cs index 9307c70f12..09a01fa39a 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/FeedAndEntryMaterializerAdapterUnitTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/FeedAndEntryMaterializerAdapterUnitTests.cs @@ -52,7 +52,8 @@ public void ValidateShortIntegrationFeedReading() var responsePipeline = new DataServiceClientResponsePipelineConfiguration(new DataServiceContext()); var odataReaderWrapper = ODataReaderWrapper.CreateForTest(testODataReader, responsePipeline); - FeedAndEntryMaterializerAdapter reader = new FeedAndEntryMaterializerAdapter(ODataFormat.Json, odataReaderWrapper, clientEdmModel, MergeOption.OverwriteChanges); + var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + FeedAndEntryMaterializerAdapter reader = new FeedAndEntryMaterializerAdapter(ODataFormat.Json, odataReaderWrapper, clientEdmModel, MergeOption.OverwriteChanges, materializerContext); int readCounter = 0; diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/MaterializerEntryTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/MaterializerEntryTests.cs index 722d589684..8a455e455b 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/MaterializerEntryTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/MaterializerEntryTests.cs @@ -13,6 +13,7 @@ namespace AstoriaUnitTests.TDD.Tests.Client.Materialization using Microsoft.OData; using ClientStrings = Microsoft.OData.Client.Strings; using Xunit; + using AstoriaUnitTests.Tests; /// <summary> /// TODO: test the rest of the functionality in <see cref="MaterializerEntry"/>. @@ -63,7 +64,8 @@ private MaterializerEntry CreateMaterializerEntry(ODataFormat format, Action<ODa modifyEntry(entry); } - return MaterializerEntry.CreateEntry(entry, format, true, this.clientModel); + var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + return MaterializerEntry.CreateEntry(entry, format, true, this.clientModel, materializerContext); } } } \ No newline at end of file diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntityMaterializerUnitTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntityMaterializerUnitTests.cs index 65d05c0dcb..23d771e718 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntityMaterializerUnitTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntityMaterializerUnitTests.cs @@ -12,6 +12,7 @@ namespace AstoriaUnitTests.TDD.Tests.Client using FluentAssertions; using Microsoft.OData; using Xunit; + using AstoriaUnitTests.Tests; /// <summary> /// Unit tests for the ODataEntityMaterializerUnitTests class. @@ -30,27 +31,29 @@ public ODataEntityMaterializerUnitTests() [Fact] public void AfterEntryMaterializedShouldOccur() { + var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + foreach (ODataFormat format in new ODataFormat[] { ODataFormat.Json }) { var entity = new SimpleEntity() { ID = 1 }; - var odataEntry = CreateEntryWithMaterializerEntry(format, entity); + var odataEntry = CreateEntryWithMaterializerEntry(format, entity, materializerContext); MaterializedEntityArgs found = null; this.context.Configurations.ResponsePipeline.OnEntityMaterialized((MaterializedEntityArgs materializedEntryEventArgs) => found = materializedEntryEventArgs); - this.context.Configurations.ResponsePipeline.FireEndEntryEvents(MaterializerEntry.GetEntry(odataEntry)); + this.context.Configurations.ResponsePipeline.FireEndEntryEvents(MaterializerEntry.GetEntry(odataEntry, materializerContext)); Assert.NotNull(found); found.Entity.Should().Be(entity); found.Entry.Should().Be(odataEntry); } } - private ODataResource CreateEntryWithMaterializerEntry(ODataFormat format, object resolvedObject) + private ODataResource CreateEntryWithMaterializerEntry(ODataFormat format, object resolvedObject, IODataMaterializerContext materializerContext) { var entry = new ODataResource(); entry.Id = new Uri("http://www.odata.org/Northwind.Svc/Customer(1)"); entry.Properties = new ODataProperty[] { new ODataProperty() { Name = "ID", Value = 1 } }; - var materializerEntry = MaterializerEntry.CreateEntry(entry, format, true, this.clientModel); + var materializerEntry = MaterializerEntry.CreateEntry(entry, format, true, this.clientModel, materializerContext); materializerEntry.ResolvedObject = resolvedObject; return entry; diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntriesEntityMaterializerUnitTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntriesEntityMaterializerUnitTests.cs index 28ca7c8f93..0b7550541b 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntriesEntityMaterializerUnitTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntriesEntityMaterializerUnitTests.cs @@ -30,9 +30,10 @@ public void ShortIntegrationTestToValidateEntryShouldBeRead() var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); - MaterializerEntry.CreateEntry(odataEntry, ODataFormat.Json, true, clientEdmModel); - var materializerContext = new TestMaterializerContext() { Model = clientEdmModel, Context = context }; - var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context); + var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + MaterializerEntry.CreateEntry(odataEntry, ODataFormat.Json, true, clientEdmModel, materializerContext); + + var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context, materializerContext); QueryComponents components = new QueryComponents(new Uri("http://foo.com/Service"), new Version(4, 0), typeof(Customer), null, new Dictionary<Expression, Expression>()); var entriesMaterializer = new ODataEntriesEntityMaterializer(new ODataResource[] { odataEntry }, materializerContext, adapter, components, typeof(Customer), null, ODataFormat.Json); diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/PrimitiveValueMaterializationPolicyTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/PrimitiveValueMaterializationPolicyTests.cs index 56716e1a4b..2da7be54a6 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/PrimitiveValueMaterializationPolicyTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/PrimitiveValueMaterializationPolicyTests.cs @@ -63,7 +63,7 @@ public void TimeOfDayValueShouldMaterializeCorrectly() internal PrimitiveValueMaterializationPolicy CreatePrimitiveValueMaterializationPolicy() { - return new PrimitiveValueMaterializationPolicy(new TestMaterializerContext(), new SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter())); + return new PrimitiveValueMaterializationPolicy(new TestMaterializerContext(new MaterializerAnnotationsCache()), new SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter())); } public class UnknownPoint diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/TestMaterializerContext.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/TestMaterializerContext.cs index 93e270bd5e..67bbf93737 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/TestMaterializerContext.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/TestMaterializerContext.cs @@ -17,12 +17,13 @@ namespace AstoriaUnitTests.Tests /// </summary> internal class TestMaterializerContext : IODataMaterializerContext { - public TestMaterializerContext() + public TestMaterializerContext(MaterializerAnnotationsCache annotationsCache) { this.UndeclaredPropertyBehavior = UndeclaredPropertyBehavior.ThrowException; this.ResponsePipeline = new DataServiceClientResponsePipelineConfiguration(this); this.Model = new ClientEdmModel(ODataProtocolVersion.V4); this.Context = new DataServiceContext(); + this.AnnotationsCache = annotationsCache; } public Func<Type, string, ClientTypeAnnotation> ResolveTypeForMaterializationOverrideFunc { get; set; } @@ -50,5 +51,7 @@ public IEdmType ResolveExpectedTypeForReading(Type expectedType) } public DataServiceContext Context { get; set; } + + public MaterializerAnnotationsCache AnnotationsCache { get; private set; } } } diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/T4/ODataT4CamelCaseTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/T4/ODataT4CamelCaseTests.cs index 35a0e8edc3..3a7546d2d9 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/T4/ODataT4CamelCaseTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/T4/ODataT4CamelCaseTests.cs @@ -322,14 +322,14 @@ public void MaterializeEntityShouldWork() var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); - var materializerEntry = MaterializerEntry.CreateEntry(odataEntry, OData.ODataFormat.Json, true, clientEdmModel); + var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + var materializerEntry = MaterializerEntry.CreateEntry(odataEntry, OData.ODataFormat.Json, true, clientEdmModel, materializerContext); - MaterializerNavigationLink.CreateLink(complexP, MaterializerEntry.CreateEntry(complexResource, OData.ODataFormat.Json, true, clientEdmModel)); - MaterializerFeed.CreateFeed(complexColResourceSet, items); - MaterializerNavigationLink.CreateLink(complexColP, complexColResourceSet); + MaterializerNavigationLink.CreateLink(complexP, MaterializerEntry.CreateEntry(complexResource, OData.ODataFormat.Json, true, clientEdmModel, materializerContext), materializerContext); + MaterializerFeed.CreateFeed(complexColResourceSet, items, materializerContext); + MaterializerNavigationLink.CreateLink(complexColP, complexColResourceSet, materializerContext); - var materializerContext = new TestMaterializerContext() { Model = clientEdmModel, Context = context }; - var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context); + var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context, materializerContext); QueryComponents components = new QueryComponents(new Uri("http://foo.com/Service"), new Version(4, 0), typeof(EntityType), null, new Dictionary<Expression, Expression>()); var entriesMaterializer = new ODataEntriesEntityMaterializer(new OData.ODataResource[] { odataEntry }, materializerContext, adapter, components, typeof(EntityType), null, OData.ODataFormat.Json); @@ -381,7 +381,9 @@ public void MaterializeComplexTypeShouldWork() } } }; - var materializerEntry = MaterializerEntry.CreateEntry(complexValue, OData.ODataFormat.Json, false, new ClientEdmModel(ODataProtocolVersion.V4)); + + var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var materializerEntry = MaterializerEntry.CreateEntry(complexValue, OData.ODataFormat.Json, false, new ClientEdmModel(ODataProtocolVersion.V4), materializerContext); this.CreateEntryMaterializationPolicy().Materialize(materializerEntry, typeof(ComplexType), false); var complex = materializerEntry.ResolvedObject as ComplexType; complex.Name.Should().Be("June"); @@ -396,9 +398,10 @@ public void MaterializeEnumTypeShouldWork() { OData.ODataEnumValue enumValue = new OData.ODataEnumValue("blue"); OData.ODataProperty property = new OData.ODataProperty { Name = "enumProperty", Value = enumValue }; - var enumPolicy = new EnumValueMaterializationPolicy(new TestMaterializerContext()); + var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var enumPolicy = new EnumValueMaterializationPolicy(materializerContext); var result = enumPolicy.MaterializeEnumTypeProperty(typeof(Color), property); - property.GetMaterializedValue().Should().Be(Color.Blue); + property.GetMaterializedValue(materializerContext).Should().Be(Color.Blue); result.Should().Be(Color.Blue); } @@ -470,8 +473,8 @@ internal EntryValueMaterializationPolicy CreateEntryMaterializationPolicy(TestMa { var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext().ReConfigureForNetworkLoadingTests(); - materializerContext = materializerContext ?? new TestMaterializerContext() { Model = clientEdmModel, Context = context }; - var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context); + materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context, materializerContext); var lazyPrimitivePropertyConverter = new Microsoft.OData.Client.SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter()); var primitiveValueMaterializerPolicy = new PrimitiveValueMaterializationPolicy(materializerContext, lazyPrimitivePropertyConverter); var entryPolicy = new EntryValueMaterializationPolicy(materializerContext, adapter, lazyPrimitivePropertyConverter, null); From b542679b32c20eace385e2d151584f1d0b353b8e Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Mon, 12 Sep 2022 11:34:33 +0300 Subject: [PATCH 04/16] Pass materializerContext argument to dynamically-called methods --- .../Materialization/ODataEntityMaterializer.cs | 6 +++--- src/Microsoft.OData.Client/ProjectionPlanCompiler.cs | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs index 384a0a5a4f..318bcddde1 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs @@ -49,7 +49,7 @@ public ODataEntityMaterializer( ProjectionPlan materializeEntryPlan) : base(materializerContext, expectedType) { - this.materializeEntryPlan = materializeEntryPlan ?? CreatePlan(queryComponents); + this.materializeEntryPlan = materializeEntryPlan ?? CreatePlan(queryComponents, materializerContext); this.EntityTrackingAdapter = entityTrackingAdapter; DSClient.SimpleLazy<PrimitivePropertyConverter> converter = new DSClient.SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter()); @@ -959,7 +959,7 @@ private static void CheckEntryToAccessNotNull(MaterializerEntry entry, string na /// <summary>Creates an entry materialization plan for a given projection.</summary> /// <param name="queryComponents">Query components for plan to materialize.</param> /// <returns>A materialization plan.</returns> - private static ProjectionPlan CreatePlan(QueryComponents queryComponents) + private static ProjectionPlan CreatePlan(QueryComponents queryComponents, IODataMaterializerContext materializerContext) { // Can we have a primitive property as well? LambdaExpression projection = queryComponents.Projection; @@ -977,7 +977,7 @@ private static ProjectionPlan CreatePlan(QueryComponents queryComponents) } else { - result = ProjectionPlanCompiler.CompilePlan(projection, queryComponents.NormalizerRewrites); + result = ProjectionPlanCompiler.CompilePlan(projection, queryComponents.NormalizerRewrites, materializerContext); } result.LastSegmentType = queryComponents.LastSegmentType; diff --git a/src/Microsoft.OData.Client/ProjectionPlanCompiler.cs b/src/Microsoft.OData.Client/ProjectionPlanCompiler.cs index 4f571ae7da..7f46be6a2e 100644 --- a/src/Microsoft.OData.Client/ProjectionPlanCompiler.cs +++ b/src/Microsoft.OData.Client/ProjectionPlanCompiler.cs @@ -51,6 +51,8 @@ internal class ProjectionPlanCompiler : ALinqExpressionVisitor /// <summary>Whether the top level projection has been found.</summary> private bool topLevelProjectionFound; + IODataMaterializerContext materializerContext; + #endregion Private fields #region Constructors @@ -59,12 +61,13 @@ internal class ProjectionPlanCompiler : ALinqExpressionVisitor /// Initializes a new <see cref="ProjectionPlanCompiler"/> instance. /// </summary> /// <param name="normalizerRewrites">Rewrites introduces by normalizer.</param> - private ProjectionPlanCompiler(Dictionary<Expression, Expression> normalizerRewrites) + private ProjectionPlanCompiler(Dictionary<Expression, Expression> normalizerRewrites, IODataMaterializerContext materializerContext) { this.annotations = new Dictionary<Expression, ExpressionAnnotation>(ReferenceEqualityComparer<Expression>.Instance); this.materializerExpression = Expression.Parameter(typeof(object), "mat"); this.normalizerRewrites = normalizerRewrites; this.pathBuilder = new ProjectionPathBuilder(); + this.materializerContext = materializerContext; } #endregion Constructors @@ -75,7 +78,7 @@ private ProjectionPlanCompiler(Dictionary<Expression, Expression> normalizerRewr /// <param name="projection">Projection expression.</param> /// <param name="normalizerRewrites">Tracks rewrite-to-source rewrites introduced by expression normalizer.</param> /// <returns>A new <see cref="ProjectionPlan"/> instance.</returns> - internal static ProjectionPlan CompilePlan(LambdaExpression projection, Dictionary<Expression, Expression> normalizerRewrites) + internal static ProjectionPlan CompilePlan(LambdaExpression projection, Dictionary<Expression, Expression> normalizerRewrites, IODataMaterializerContext materializerContext) { Debug.Assert(projection != null, "projection != null"); Debug.Assert(projection.Parameters.Count == 1, "projection.Parameters.Count == 1"); @@ -88,7 +91,7 @@ internal static ProjectionPlan CompilePlan(LambdaExpression projection, Dictiona projection.Body.NodeType == ExpressionType.New, "projection.Body.NodeType == Constant, MemberInit, MemberAccess, Convert(Checked) New"); - ProjectionPlanCompiler rewriter = new ProjectionPlanCompiler(normalizerRewrites); + ProjectionPlanCompiler rewriter = new ProjectionPlanCompiler(normalizerRewrites, materializerContext); #if TRACE_CLIENT_PROJECTIONS Trace.WriteLine("Projection: " + projection); #endif @@ -666,7 +669,8 @@ private Expression RebindEntityMemberInit(MemberInitExpression init) Expression nestedEntry = CallMaterializer( "ProjectionGetEntry", entryParameterAtMemberInit, - Expression.Constant(assignment.Member.Name, typeof(string))); + Expression.Constant(assignment.Member.Name, typeof(string)), + Expression.Constant(this.materializerContext)); ParameterExpression nestedEntryParameter = Expression.Parameter( typeof(object), "subentry" + (this.identifierId++).ToString(CultureInfo.InvariantCulture)); From eabc99ba920092b6e53a8fb3a197c76a4a181fb2 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Thu, 15 Sep 2022 06:58:22 +0300 Subject: [PATCH 05/16] Make MaterializerAnnotatonsCache sealed --- .../Materialization/MaterializerAnnotationsCache.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs index 1c14b2e349..b81ac617c5 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs @@ -1,14 +1,9 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.OData.Client.Materialization { - internal class MaterializerAnnotationsCache + internal sealed class MaterializerAnnotationsCache { private readonly Dictionary<ODataAnnotatable, object> cache = new Dictionary<ODataAnnotatable, object>(ReferenceEqualityComparer<ODataAnnotatable>.Instance); From b2bf1ee351579c22726af1b0f5f38968bddd9634 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Thu, 15 Sep 2022 07:27:40 +0300 Subject: [PATCH 06/16] Add doc comments to MaterializerAnnotationsCache --- .../MaterializerAnnotationsCache.cs | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs index b81ac617c5..98490e4b7d 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs @@ -3,21 +3,55 @@ namespace Microsoft.OData.Client.Materialization { + /// <summary> + /// A cache used to store temporary materialization metadata + /// for deseriliazed <see cref="ODataItem"/>s during materialization of response payloads + /// into client CLR objects. Keys are identified using reference equality. + /// </summary> + /// <remarks> + /// Instances of the cache are not thread-safe. Each instance is expected to be used within the + /// scope of a single request. + /// + /// The cache is designed to store one entry type per key. For example, if the key is + /// an <see cref="ODataResource"/>, its value should be a <see cref="MaterializerEntry"/>. It also + /// expects the value for any given key will not change. + /// So you should call `cache.SetAnnotation(odataResource, materializerEntry)` add the cache entry + /// and `cache.GetAnnotations<MaterializerEntry>(odataResource)` to retrieve the entry. + /// + /// If the requirements change such that we need to allow updating the value of a key to a different type, + /// then the cache should be redesigned. + /// </remarks> internal sealed class MaterializerAnnotationsCache { private readonly Dictionary<ODataAnnotatable, object> cache = new Dictionary<ODataAnnotatable, object>(ReferenceEqualityComparer<ODataAnnotatable>.Instance); + /// <summary> + /// Adds an entry to the cache with the <paramref name="annotatable"/> + /// set as key, + /// </summary> + /// <typeparam name="T">The type of the value.</typeparam> + /// <param name="annotatable">The key of the entry.</param> + /// <param name="value">The value to associate with the key.</param> public void SetAnnotation<T>(ODataAnnotatable annotatable, T value) where T : class { + Debug.Assert(annotatable != null, "annotatable != null"); this.cache.Add(annotatable, value); } + /// <summary> + /// Retrieves the value associated with the specified <paramref name="annotatable"/>. + /// Returns null if the cache does not contain an entry with the specifiedy key. + /// </summary> + /// <typeparam name="T">The expected type of the value.</typeparam> + /// <param name="annotatable"></param> + /// <returns>The value associated with the specified <paramref name="annotatable"/> if the entry exists, or null otherwise.</returns> public T GetAnnotation<T>(ODataAnnotatable annotatable) where T: class { if (this.cache.TryGetValue(annotatable, out object value)) { - Debug.Assert(value is T); - return value as T; + T valueAsT = value as T; + Debug.Assert(valueAsT != null, "valueAsT != null"); + return valueAsT; } return default(T); @@ -26,11 +60,26 @@ public T GetAnnotation<T>(ODataAnnotatable annotatable) where T: class internal static class MaterializerContextExtensions { + /// <summary> + /// Associates the specified annotation <paramref name="value"/> with the specified + /// <paramref name="annotatable"/> to store metadata used for materialization. + /// </summary> + /// <typeparam name="T">The type of the value.</typeparam> + /// <param name="context">The materializer context.</param> + /// <param name="annotatable">The item to annotate.</param> + /// <param name="value">The annotation value.</param> public static void SetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable, T value) where T : class { context.AnnotationsCache.SetAnnotation(annotatable, value); } + /// <summary> + /// Retrieves the annotation value associated with te specified <paramref name="annotatable"/>. + /// </summary> + /// <typeparam name="T">The expected type of the annotation value.</typeparam> + /// <param name="context">The materializer context.</param> + /// <param name="annotatable">The item for which to retrieve the annotation.</param> + /// <returns>The annotation value associated with the <paramref name="annotatable"/> if it exists, or null otherwise.</returns> public static T GetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable) where T : class { return context.AnnotationsCache.GetAnnotation<T>(annotatable); From 53803d9c8ef0611e522ffc9f40822f36396743f4 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Thu, 15 Sep 2022 07:32:37 +0300 Subject: [PATCH 07/16] Remove ODataAnnotatableExtensions --- .../MaterializerAnnotationsCache.cs | 36 +++---------- .../MaterializerContextExtensions.cs | 36 +++++++++++++ .../ODataAnnotatableExtensions.cs | 53 ------------------- 3 files changed, 43 insertions(+), 82 deletions(-) create mode 100644 src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs delete mode 100644 src/Microsoft.OData.Client/ODataAnnotatableExtensions.cs diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs index 98490e4b7d..5f8599f6b2 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs @@ -1,4 +1,10 @@ -using System.Collections.Generic; +//--------------------------------------------------------------------- +// <copyright file="MaterializerAnnotationsCache.cs" company="Microsoft"> +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// </copyright> +//--------------------------------------------------------------------- + +using System.Collections.Generic; using System.Diagnostics; namespace Microsoft.OData.Client.Materialization @@ -57,32 +63,4 @@ public T GetAnnotation<T>(ODataAnnotatable annotatable) where T: class return default(T); } } - - internal static class MaterializerContextExtensions - { - /// <summary> - /// Associates the specified annotation <paramref name="value"/> with the specified - /// <paramref name="annotatable"/> to store metadata used for materialization. - /// </summary> - /// <typeparam name="T">The type of the value.</typeparam> - /// <param name="context">The materializer context.</param> - /// <param name="annotatable">The item to annotate.</param> - /// <param name="value">The annotation value.</param> - public static void SetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable, T value) where T : class - { - context.AnnotationsCache.SetAnnotation(annotatable, value); - } - - /// <summary> - /// Retrieves the annotation value associated with te specified <paramref name="annotatable"/>. - /// </summary> - /// <typeparam name="T">The expected type of the annotation value.</typeparam> - /// <param name="context">The materializer context.</param> - /// <param name="annotatable">The item for which to retrieve the annotation.</param> - /// <returns>The annotation value associated with the <paramref name="annotatable"/> if it exists, or null otherwise.</returns> - public static T GetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable) where T : class - { - return context.AnnotationsCache.GetAnnotation<T>(annotatable); - } - } } diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs b/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs new file mode 100644 index 0000000000..1a2c7b72c2 --- /dev/null +++ b/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs @@ -0,0 +1,36 @@ +//--------------------------------------------------------------------- +// <copyright file="MaterializerContextExtensions.cs" company="Microsoft"> +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// </copyright> +//--------------------------------------------------------------------- + +namespace Microsoft.OData.Client.Materialization +{ + internal static class MaterializerContextExtensions + { + /// <summary> + /// Associates the specified annotation <paramref name="value"/> with the specified + /// <paramref name="annotatable"/> to store metadata used for materialization. + /// </summary> + /// <typeparam name="T">The type of the value.</typeparam> + /// <param name="context">The materializer context.</param> + /// <param name="annotatable">The item to annotate.</param> + /// <param name="value">The annotation value.</param> + public static void SetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable, T value) where T : class + { + context.AnnotationsCache.SetAnnotation(annotatable, value); + } + + /// <summary> + /// Retrieves the annotation value associated with te specified <paramref name="annotatable"/>. + /// </summary> + /// <typeparam name="T">The expected type of the annotation value.</typeparam> + /// <param name="context">The materializer context.</param> + /// <param name="annotatable">The item for which to retrieve the annotation.</param> + /// <returns>The annotation value associated with the <paramref name="annotatable"/> if it exists, or null otherwise.</returns> + public static T GetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable) where T : class + { + return context.AnnotationsCache.GetAnnotation<T>(annotatable); + } + } +} diff --git a/src/Microsoft.OData.Client/ODataAnnotatableExtensions.cs b/src/Microsoft.OData.Client/ODataAnnotatableExtensions.cs deleted file mode 100644 index eacb02dc99..0000000000 --- a/src/Microsoft.OData.Client/ODataAnnotatableExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -//--------------------------------------------------------------------- -// <copyright file="ODataAnnotatableExtensions.cs" company="Microsoft"> -// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. -// </copyright> -//--------------------------------------------------------------------- - -using System.Runtime.CompilerServices; -using System.Diagnostics; - -namespace Microsoft.OData.Client -{ - internal static class ODataAnnotatableExtensions - { - //public static void SetAnnotation<T>(this ODataAnnotatable annotatable, T annotation) - // where T : class - //{ - // Debug.Assert(annotatable != null, "annotatable != null"); - // Debug.Assert(annotation != null, "annotation != null"); - - // InternalDictionary<T>.SetAnnotation(annotatable, annotation); - //} - - //public static T GetAnnotation<T>(this ODataAnnotatable annotatable) - // where T : class - //{ - // Debug.Assert(annotatable != null, "annotatable != null"); - - // return InternalDictionary<T>.GetAnnotation(annotatable); - //} - - private static class InternalDictionary<T> where T : class - { - private static readonly ConditionalWeakTable<ODataAnnotatable, T> Dictionary = - new ConditionalWeakTable<ODataAnnotatable, T>(); - - public static void SetAnnotation(ODataAnnotatable annotatable, T annotation) - { - Dictionary.Add(annotatable, annotation); - } - - public static T GetAnnotation(ODataAnnotatable annotatable) - { - T annotation; - if (Dictionary.TryGetValue(annotatable, out annotation)) - { - return annotation; - } - - return default(T); - } - } - } -} From 5a66220ec6e836271301854045bbf3d83f1c3187 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Thu, 15 Sep 2022 08:40:22 +0300 Subject: [PATCH 08/16] Update doc comments --- src/Microsoft.OData.Client/AtomMaterializerLog.cs | 4 ++++ src/Microsoft.OData.Client/BaseAsyncResult.cs | 3 +++ src/Microsoft.OData.Client/BaseSaveResult.cs | 2 -- src/Microsoft.OData.Client/DataServiceRequest.cs | 1 + .../Materialization/EntityTrackingAdapter.cs | 1 + .../Materialization/FeedAndEntryMaterializerAdapter.cs | 7 ++++++- .../Materialization/MaterializerEntry.cs | 2 ++ .../Materialization/MaterializerFeed.cs | 2 ++ .../Materialization/MaterializerNavigationLink.cs | 9 +++++++-- .../Materialization/ODataEntityMaterializer.cs | 2 ++ .../Materialization/ODataEntityMaterializerInvoker.cs | 2 ++ .../Materialization/ODataItemExtensions.cs | 10 ++++++++-- .../Materialization/ODataReaderEntityMaterializer.cs | 1 + src/Microsoft.OData.Client/MaterializeFromAtom.cs | 2 ++ src/Microsoft.OData.Client/ProjectionPlanCompiler.cs | 6 +++++- 15 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.OData.Client/AtomMaterializerLog.cs b/src/Microsoft.OData.Client/AtomMaterializerLog.cs index 0b79b25428..5f39833c57 100644 --- a/src/Microsoft.OData.Client/AtomMaterializerLog.cs +++ b/src/Microsoft.OData.Client/AtomMaterializerLog.cs @@ -46,6 +46,9 @@ internal class AtomMaterializerLog /// <summary>Target instance to refresh.</summary> private object insertRefreshObject; + /// <summary> + /// The materializer context. + /// </summary> private IODataMaterializerContext materializerContext; #endregion Private fields @@ -58,6 +61,7 @@ internal class AtomMaterializerLog /// <param name="mergeOption">The merge option for the log.</param> /// <param name="model">The model for the log.</param> /// <param name="entityTracker">The entity tracker for the log.</param> + /// <param name="materializerContext">The materializer context.</param> /// <remarks> /// Note that the merge option can't be changed. /// </remarks> diff --git a/src/Microsoft.OData.Client/BaseAsyncResult.cs b/src/Microsoft.OData.Client/BaseAsyncResult.cs index aa31139238..323c792089 100644 --- a/src/Microsoft.OData.Client/BaseAsyncResult.cs +++ b/src/Microsoft.OData.Client/BaseAsyncResult.cs @@ -27,6 +27,9 @@ internal abstract class BaseAsyncResult : IAsyncResult /// <summary>wrapped request</summary> protected PerRequest perRequest; + /// <summary> + /// Cache used to store temporary metadata for OData items during materialization. + /// </summary> protected MaterializerAnnotationsCache annotationsCache = new MaterializerAnnotationsCache(); /// <summary> diff --git a/src/Microsoft.OData.Client/BaseSaveResult.cs b/src/Microsoft.OData.Client/BaseSaveResult.cs index 9f8f46406e..5f44c01272 100644 --- a/src/Microsoft.OData.Client/BaseSaveResult.cs +++ b/src/Microsoft.OData.Client/BaseSaveResult.cs @@ -67,8 +67,6 @@ internal abstract class BaseSaveResult : BaseAsyncResult /// <summary>application/json Content-Type header</summary> private const string JsonContentTypeHeader = "application/json"; - protected MaterializerAnnotationsCache annotationsCache = new MaterializerAnnotationsCache(); - #endregion Private Fields /// <summary> diff --git a/src/Microsoft.OData.Client/DataServiceRequest.cs b/src/Microsoft.OData.Client/DataServiceRequest.cs index 0b6c120703..273202ee4f 100644 --- a/src/Microsoft.OData.Client/DataServiceRequest.cs +++ b/src/Microsoft.OData.Client/DataServiceRequest.cs @@ -60,6 +60,7 @@ internal ODataPayloadKind PayloadKind /// <param name="contentType">contentType</param> /// <param name="message">the message</param> /// <param name="expectedPayloadKind">expected payload kind.</param> + /// <param name="annotationsCache">The annotations cache used to store temporary metadata used for materialization of OData items.</param> /// <returns>atom materializer</returns> internal static MaterializeAtom Materialize( ResponseInfo responseInfo, diff --git a/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs b/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs index be2e6e2bbf..991ca74272 100644 --- a/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs +++ b/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs @@ -27,6 +27,7 @@ internal class EntityTrackingAdapter /// <param name="mergeOption">The merge option.</param> /// <param name="model">The model.</param> /// <param name="context">The context.</param> + /// <param name="materializerContext">The materialization context used to retrieve materialization-related information.</param> internal EntityTrackingAdapter(EntityTrackerBase entityTracker, MergeOption mergeOption, ClientEdmModel model, DataServiceContext context, IODataMaterializerContext materializerContext) { this.MaterializationLog = new AtomMaterializerLog(mergeOption, model, entityTracker, materializerContext); diff --git a/src/Microsoft.OData.Client/Materialization/FeedAndEntryMaterializerAdapter.cs b/src/Microsoft.OData.Client/Materialization/FeedAndEntryMaterializerAdapter.cs index 19815f4299..7db16572fb 100644 --- a/src/Microsoft.OData.Client/Materialization/FeedAndEntryMaterializerAdapter.cs +++ b/src/Microsoft.OData.Client/Materialization/FeedAndEntryMaterializerAdapter.cs @@ -39,7 +39,10 @@ internal class FeedAndEntryMaterializerAdapter /// <summary>The current entry.</summary> private ODataResource currentEntry; - private IODataMaterializerContext materializerContext; + /// <summary> + /// The materializer context. + /// </summary> + private readonly IODataMaterializerContext materializerContext; /// <summary> /// Initializes a new instance of the <see cref="FeedAndEntryMaterializerAdapter"/> class. @@ -48,6 +51,7 @@ internal class FeedAndEntryMaterializerAdapter /// <param name="reader">The reader.</param> /// <param name="model">The model.</param> /// <param name="mergeOption">The mergeOption.</param> + /// <param name="materializerContext">The materializer context.</param> internal FeedAndEntryMaterializerAdapter(ODataMessageReader messageReader, ODataReaderWrapper reader, ClientEdmModel model, MergeOption mergeOption, IODataMaterializerContext materializerContext) : this(ODataUtils.GetReadFormat(messageReader), reader, model, mergeOption, materializerContext) { @@ -60,6 +64,7 @@ internal FeedAndEntryMaterializerAdapter(ODataMessageReader messageReader, OData /// <param name="reader">The reader.</param> /// <param name="model">The model.</param> /// <param name="mergeOption">The mergeOption.</param> + /// <param name="materializerContext">The materializer context.</param> internal FeedAndEntryMaterializerAdapter(ODataFormat odataFormat, ODataReaderWrapper reader, ClientEdmModel model, MergeOption mergeOption, IODataMaterializerContext materializerContext) { this.readODataFormat = odataFormat; diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs b/src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs index 0106ea7ef8..224c56d5cc 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs @@ -224,6 +224,7 @@ public static MaterializerEntry CreateEmpty() /// <param name="format">The format the entry was read in.</param> /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param> /// <param name="model">The client model.</param> + /// <param name="materializerContext">The current materializer context.</param> /// <returns>A new materializer entry.</returns> public static MaterializerEntry CreateEntry(ODataResource entry, ODataFormat format, bool isTracking, ClientEdmModel model, IODataMaterializerContext materializerContext) { @@ -251,6 +252,7 @@ public static MaterializerEntry CreateEntryForLoadProperty(EntityDescriptor desc /// Gets an entry for a given ODataResource. /// </summary> /// <param name="entry">The ODataResource.</param> + /// <param name="materializerContext">The current materializer context.</param> /// <returns>The materializer entry</returns> public static MaterializerEntry GetEntry(ODataResource entry, IODataMaterializerContext materializerContext) { diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerFeed.cs b/src/Microsoft.OData.Client/Materialization/MaterializerFeed.cs index adf624e3bf..cddbe97150 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerFeed.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerFeed.cs @@ -67,6 +67,7 @@ public Uri NextPageLink /// </summary> /// <param name="feed">The feed.</param> /// <param name="entries">The entries.</param> + /// <param name="materializerContext">The current materializer context.</param> /// <returns>The materializer feed.</returns> public static MaterializerFeed CreateFeed(ODataResourceSet feed, IEnumerable<ODataResource> entries, IODataMaterializerContext materializerContext) { @@ -87,6 +88,7 @@ public static MaterializerFeed CreateFeed(ODataResourceSet feed, IEnumerable<ODa /// Gets the materializer feed. /// </summary> /// <param name="feed">The feed.</param> + /// <param name="materializerContext">The current materializer context.</param> /// <returns>The materializer feed.</returns> public static MaterializerFeed GetFeed(ODataResourceSet feed, IODataMaterializerContext materializerContext) { diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerNavigationLink.cs b/src/Microsoft.OData.Client/Materialization/MaterializerNavigationLink.cs index ec4b360b14..9ad1c49d4d 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerNavigationLink.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerNavigationLink.cs @@ -63,10 +63,12 @@ public ODataResourceSet Feed /// </summary> /// <param name="link">The link.</param> /// <param name="entry">The entry.</param> + /// <param name="materializerContext">The current materializer context.</param> /// <returns>The materializer link.</returns> public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link, MaterializerEntry entry, IODataMaterializerContext materializerContext) { - Debug.Assert(materializerContext.GetAnnotation<MaterializerNavigationLink>(link) == null, "there should be no MaterializerNestedResourceInfo annotation on the entry link yet"); + Debug.Assert(materializerContext.GetAnnotation<MaterializerNavigationLink>(link) == null, + "there should be no MaterializerNestedResourceInfo annotation on the entry link yet"); MaterializerNavigationLink materializedNestedResourceInfo = new MaterializerNavigationLink(link, entry); materializerContext.SetAnnotation<MaterializerNavigationLink>(link, materializedNestedResourceInfo); return materializedNestedResourceInfo; @@ -77,10 +79,12 @@ public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link /// </summary> /// <param name="link">The link.</param> /// <param name="resourceSet">The resource set.</param> + /// <param name="materializerContext">The current materializer context.</param> /// <returns>The materializer link.</returns> public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link, ODataResourceSet resourceSet, IODataMaterializerContext materializerContext) { - Debug.Assert(materializerContext.GetAnnotation<MaterializerNavigationLink>(link) == null, "there should be no MaterializerNestedResourceInfo annotation on the feed link yet"); + Debug.Assert(materializerContext.GetAnnotation<MaterializerNavigationLink>(link) == null, + "there should be no MaterializerNestedResourceInfo annotation on the feed link yet"); MaterializerNavigationLink materializedNestedResourceInfo = new MaterializerNavigationLink(link, resourceSet); materializerContext.SetAnnotation<MaterializerNavigationLink>(link, materializedNestedResourceInfo); return materializedNestedResourceInfo; @@ -90,6 +94,7 @@ public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link /// Gets the materializer link. /// </summary> /// <param name="link">The link.</param> + /// <param name="materializerContext">The current materializer context.</param> /// <returns>The materializer link.</returns> public static MaterializerNavigationLink GetLink(ODataNestedResourceInfo link, IODataMaterializerContext materializerContext) { diff --git a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs index 318bcddde1..8ba5d5488f 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializer.cs @@ -230,6 +230,7 @@ internal static ProjectionPlan CreatePlanForShallowMaterialization(Type lastSegm /// <param name="entry">Root entry for paths.</param> /// <param name="expectedType">Expected type for <paramref name="entry"/>.</param> /// <param name="path">Path to pull value for.</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns>Whether the specified <paramref name="path"/> is null.</returns> /// <remarks> /// This method will not instantiate entity types on the path. @@ -384,6 +385,7 @@ internal static IEnumerable ProjectionSelect( /// <summary>Provides support for getting payload entries during projections.</summary> /// <param name="entry">Entry to get sub-entry from.</param> /// <param name="name">Name of sub-entry.</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns>The sub-entry (never null).</returns> internal static ODataResource ProjectionGetEntry(MaterializerEntry entry, string name, IODataMaterializerContext materializerContext) { diff --git a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializerInvoker.cs b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializerInvoker.cs index 046ef198c7..4a9e4b4372 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializerInvoker.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataEntityMaterializerInvoker.cs @@ -52,6 +52,7 @@ internal static List<TTarget> ListAsElementType<T, TTarget>(object materializer, /// <param name="entry">Root entry for paths.</param> /// <param name="expectedType">Expected type for <paramref name="entry"/>.</param> /// <param name="path">Path to pull value for.</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns>Whether the specified <paramref name="path"/> is null.</returns> /// <remarks> /// This method will not instantiate entity types on the path. @@ -93,6 +94,7 @@ internal static IEnumerable ProjectionSelect( /// <summary>Provides support for getting payload entries during projections.</summary> /// <param name="entry">Entry to get sub-entry from.</param> /// <param name="name">Name of sub-entry.</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns>The sub-entry (never null).</returns> internal static object ProjectionGetEntry(object entry, string name, IODataMaterializerContext materializerContext) { diff --git a/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs b/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs index 713b2673e7..a48beae608 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs @@ -18,6 +18,7 @@ internal static class ODataItemExtensions /// Gets the materialized value. /// </summary> /// <param name="property">The property.</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns>The materialized value.</returns> public static object GetMaterializedValue(this ODataProperty property, IODataMaterializerContext materializerContext) { @@ -29,6 +30,7 @@ public static object GetMaterializedValue(this ODataProperty property, IODataMat /// Determines whether a value has been materialized. /// </summary> /// <param name="property">The property.</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns><c>true</c> if the value has been materialized; otherwise, <c>false</c>.</returns> public static bool HasMaterializedValue(this ODataProperty property, IODataMaterializerContext materializerContext) { @@ -40,7 +42,8 @@ public static bool HasMaterializedValue(this ODataProperty property, IODataMater /// Sets the materialized value. /// </summary> /// <param name="property">The property.</param> - /// <param name="materializedValue">The materialized value.</param> + /// <param name="materializedValue">The materializer value.</param> + /// <param name="materializerContext">The materializer context.</param> public static void SetMaterializedValue(this ODataProperty property, object materializedValue, IODataMaterializerContext materializerContext) { ODataAnnotatable annotatableObject = property.Value as ODataAnnotatable ?? property; @@ -51,10 +54,11 @@ public static void SetMaterializedValue(this ODataProperty property, object mate /// Gets the materialized value. /// </summary> /// <param name="annotatableObject">The annotatable object.</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns>The materialized value</returns> private static object GetMaterializedValueCore(ODataAnnotatable annotatableObject, IODataMaterializerContext materializerContext) { - MaterializerPropertyValue value = materializerContext.GetAnnotation<MaterializerPropertyValue>(annotatableObject); + MaterializerPropertyValue value = materializerContext.AnnotationsCache.GetAnnotation<MaterializerPropertyValue>(annotatableObject); Debug.Assert(value != null, "MaterializedValue not set"); return value.Value; } @@ -63,6 +67,7 @@ private static object GetMaterializedValueCore(ODataAnnotatable annotatableObjec /// Determines whether a value has been materialized. /// </summary> /// <param name="annotatableObject">The annotatable object.</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns><c>true</c> if the value has been materialized; otherwise, <c>false</c>.</returns> private static bool HasMaterializedValueCore(ODataAnnotatable annotatableObject, IODataMaterializerContext materializerContext) { @@ -74,6 +79,7 @@ private static bool HasMaterializedValueCore(ODataAnnotatable annotatableObject, /// </summary> /// <param name="annotatableObject">The annotatable object.</param> /// <param name="materializedValue">The materialized value.</param> + /// <param name="materializerContext">The materializer context.</param> private static void SetMaterializedValueCore(ODataAnnotatable annotatableObject, object materializedValue, IODataMaterializerContext materializerContext) { MaterializerPropertyValue materializerValue = new MaterializerPropertyValue { Value = materializedValue }; diff --git a/src/Microsoft.OData.Client/Materialization/ODataReaderEntityMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataReaderEntityMaterializer.cs index f474624db7..7759e67388 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataReaderEntityMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataReaderEntityMaterializer.cs @@ -115,6 +115,7 @@ protected override ODataFormat Format /// <param name="message">the message for the payload</param> /// <param name="responseInfo">The current ResponseInfo object</param> /// <param name="expectedType">The expected type</param> + /// <param name="materializerContext">The materializer context.</param> /// <returns>the MaterializerEntry that was read</returns> internal static MaterializerEntry ParseSingleEntityPayload(IODataResponseMessage message, ResponseInfo responseInfo, Type expectedType, IODataMaterializerContext materializerContext) { diff --git a/src/Microsoft.OData.Client/MaterializeFromAtom.cs b/src/Microsoft.OData.Client/MaterializeFromAtom.cs index 401a4e0a5b..f2fa5b85bb 100644 --- a/src/Microsoft.OData.Client/MaterializeFromAtom.cs +++ b/src/Microsoft.OData.Client/MaterializeFromAtom.cs @@ -74,6 +74,7 @@ internal class MaterializeAtom : IDisposable, IEnumerable, IEnumerator /// <param name="plan">Projection plan (if compiled in an earlier query).</param> /// <param name="responseMessage">responseMessage</param> /// <param name="payloadKind">The kind of the payload to materialize.</param> + /// <param name="annotationsCache">The annotations cache used to store and retrieve temporary metadata used for materialization of OData items.</param> internal MaterializeAtom( ResponseInfo responseInfo, QueryComponents queryComponents, @@ -102,6 +103,7 @@ internal MaterializeAtom( /// <param name="entries">entries that needs to be materialized.</param> /// <param name="elementType">result type.</param> /// <param name="format">The format of the response being materialized from.</param> + /// <param name="annotationsCache">The annotations cache used to store and retrieve temporary metadata used for materialization of OData items.</param> internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> entries, Type elementType, ODataFormat format, MaterializerAnnotationsCache annotationsCache) { this.responseInfo = responseInfo; diff --git a/src/Microsoft.OData.Client/ProjectionPlanCompiler.cs b/src/Microsoft.OData.Client/ProjectionPlanCompiler.cs index 7f46be6a2e..9051246250 100644 --- a/src/Microsoft.OData.Client/ProjectionPlanCompiler.cs +++ b/src/Microsoft.OData.Client/ProjectionPlanCompiler.cs @@ -51,7 +51,10 @@ internal class ProjectionPlanCompiler : ALinqExpressionVisitor /// <summary>Whether the top level projection has been found.</summary> private bool topLevelProjectionFound; - IODataMaterializerContext materializerContext; + /// <summary> + /// The materializer context. + /// </summary> + private readonly IODataMaterializerContext materializerContext; #endregion Private fields @@ -61,6 +64,7 @@ internal class ProjectionPlanCompiler : ALinqExpressionVisitor /// Initializes a new <see cref="ProjectionPlanCompiler"/> instance. /// </summary> /// <param name="normalizerRewrites">Rewrites introduces by normalizer.</param> + /// <param name="materializerContext">The materializer context.</param> private ProjectionPlanCompiler(Dictionary<Expression, Expression> normalizerRewrites, IODataMaterializerContext materializerContext) { this.annotations = new Dictionary<Expression, ExpressionAnnotation>(ReferenceEqualityComparer<Expression>.Instance); From 819ca9673c1173767dfaf45a7b53d93a3c33d1bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Habinshuti?= <haby_habbes@live.com> Date: Sat, 17 Sep 2022 19:09:49 +0300 Subject: [PATCH 09/16] Update src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs Co-authored-by: Elizabeth Okerio <elizaokerio@gmail.com> --- .../Materialization/EntityTrackingAdapter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs b/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs index 991ca74272..1f9b5b5f33 100644 --- a/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs +++ b/src/Microsoft.OData.Client/Materialization/EntityTrackingAdapter.cs @@ -27,7 +27,8 @@ internal class EntityTrackingAdapter /// <param name="mergeOption">The merge option.</param> /// <param name="model">The model.</param> /// <param name="context">The context.</param> - /// <param name="materializerContext">The materialization context used to retrieve materialization-related information.</param> + /// <param name="materializerContext">The materialization context used to retrieve materialization related information.</param> + internal EntityTrackingAdapter(EntityTrackerBase entityTracker, MergeOption mergeOption, ClientEdmModel model, DataServiceContext context, IODataMaterializerContext materializerContext) { this.MaterializationLog = new AtomMaterializerLog(mergeOption, model, entityTracker, materializerContext); From e8c579219b52a5c8e95b592a87c9e93243bed7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Habinshuti?= <haby_habbes@live.com> Date: Sat, 17 Sep 2022 19:18:13 +0300 Subject: [PATCH 10/16] Apply suggestions from code review Co-authored-by: John Gathogo <john.gathogo@gmail.com> --- .../Materialization/MaterializerAnnotationsCache.cs | 13 ++++++------- .../MaterializerContextExtensions.cs | 6 ++++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs index 5f8599f6b2..025a9de27e 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs @@ -11,19 +11,18 @@ namespace Microsoft.OData.Client.Materialization { /// <summary> /// A cache used to store temporary materialization metadata - /// for deseriliazed <see cref="ODataItem"/>s during materialization of response payloads + /// for deserialized <see cref="ODataItem"/>s during materialization of response payloads + /// into client CLR objects. Keys are identified using reference equality. /// </summary> /// <remarks> /// Instances of the cache are not thread-safe. Each instance is expected to be used within the /// scope of a single request. - /// /// The cache is designed to store one entry type per key. For example, if the key is /// an <see cref="ODataResource"/>, its value should be a <see cref="MaterializerEntry"/>. It also /// expects the value for any given key will not change. - /// So you should call `cache.SetAnnotation(odataResource, materializerEntry)` add the cache entry + /// For this reason, you should call `cache.SetAnnotation(odataResource, materializerEntry)` to add a cache entry /// and `cache.GetAnnotations<MaterializerEntry>(odataResource)` to retrieve the entry. - /// /// If the requirements change such that we need to allow updating the value of a key to a different type, /// then the cache should be redesigned. /// </remarks> @@ -33,7 +32,7 @@ internal sealed class MaterializerAnnotationsCache /// <summary> /// Adds an entry to the cache with the <paramref name="annotatable"/> - /// set as key, + /// set as key. /// </summary> /// <typeparam name="T">The type of the value.</typeparam> /// <param name="annotatable">The key of the entry.</param> @@ -46,11 +45,11 @@ public void SetAnnotation<T>(ODataAnnotatable annotatable, T value) where T : cl /// <summary> /// Retrieves the value associated with the specified <paramref name="annotatable"/>. - /// Returns null if the cache does not contain an entry with the specifiedy key. + /// Returns null if the cache does not contain an entry with the specified key. /// </summary> /// <typeparam name="T">The expected type of the value.</typeparam> /// <param name="annotatable"></param> - /// <returns>The value associated with the specified <paramref name="annotatable"/> if the entry exists, or null otherwise.</returns> + /// <returns>The value associated with the specified <paramref name="annotatable"/> if the entry exists; otherwise null.</returns> public T GetAnnotation<T>(ODataAnnotatable annotatable) where T: class { if (this.cache.TryGetValue(annotatable, out object value)) diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs b/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs index 1a2c7b72c2..81ed8d029e 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs @@ -9,7 +9,8 @@ namespace Microsoft.OData.Client.Materialization internal static class MaterializerContextExtensions { /// <summary> - /// Associates the specified annotation <paramref name="value"/> with the specified + /// Associates the specified <paramref name="value"/> with the specified + /// <paramref name="annotatable"/> to store metadata used for materialization. /// </summary> /// <typeparam name="T">The type of the value.</typeparam> @@ -22,7 +23,8 @@ public static void SetAnnotation<T>(this IODataMaterializerContext context, ODat } /// <summary> - /// Retrieves the annotation value associated with te specified <paramref name="annotatable"/>. + /// Retrieves the value associated with the specified <paramref name="annotatable"/>. + /// </summary> /// <typeparam name="T">The expected type of the annotation value.</typeparam> /// <param name="context">The materializer context.</param> From 13ced4dc60d716feba419be49092430516474088 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Sat, 17 Sep 2022 19:37:03 +0300 Subject: [PATCH 11/16] Implement review suggestions --- src/Microsoft.OData.Client/BaseSaveResult.cs | 1 - .../DataServiceRequest.cs | 1 - .../MaterializerAnnotationsCache.cs | 2 +- .../MaterializeFromAtom.cs | 6 +++--- ...ataServiceContextNoTrackingStreamsTests.cs | 2 +- ...ializationPolicyForComplexResourceTests.cs | 20 +++++++++---------- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/Microsoft.OData.Client/BaseSaveResult.cs b/src/Microsoft.OData.Client/BaseSaveResult.cs index 5f44c01272..247c8c163b 100644 --- a/src/Microsoft.OData.Client/BaseSaveResult.cs +++ b/src/Microsoft.OData.Client/BaseSaveResult.cs @@ -19,7 +19,6 @@ namespace Microsoft.OData.Client using System.Text; using System.Threading; using Microsoft.OData; - using Microsoft.OData.Client.Materialization; using Microsoft.OData.Client.Metadata; #endregion Namespaces diff --git a/src/Microsoft.OData.Client/DataServiceRequest.cs b/src/Microsoft.OData.Client/DataServiceRequest.cs index 273202ee4f..d68b3105de 100644 --- a/src/Microsoft.OData.Client/DataServiceRequest.cs +++ b/src/Microsoft.OData.Client/DataServiceRequest.cs @@ -79,7 +79,6 @@ internal static MaterializeAtom Materialize( { return MaterializeAtom.EmptyResults; } - return new MaterializeAtom(responseInfo, queryComponents, plan, message, expectedPayloadKind, annotationsCache); } diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs index 025a9de27e..8911320329 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs @@ -48,7 +48,7 @@ public void SetAnnotation<T>(ODataAnnotatable annotatable, T value) where T : cl /// Returns null if the cache does not contain an entry with the specified key. /// </summary> /// <typeparam name="T">The expected type of the value.</typeparam> - /// <param name="annotatable"></param> + /// <param name="annotatable">The key of the entry.</param> /// <returns>The value associated with the specified <paramref name="annotatable"/> if the entry exists; otherwise null.</returns> public T GetAnnotation<T>(ODataAnnotatable annotatable) where T: class { diff --git a/src/Microsoft.OData.Client/MaterializeFromAtom.cs b/src/Microsoft.OData.Client/MaterializeFromAtom.cs index f2fa5b85bb..aff289e1d3 100644 --- a/src/Microsoft.OData.Client/MaterializeFromAtom.cs +++ b/src/Microsoft.OData.Client/MaterializeFromAtom.cs @@ -113,9 +113,9 @@ internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> e Type implementationType; Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType); QueryComponents qc = new QueryComponents(null, Util.ODataVersionEmpty, elementType, null, null); - ODataMaterializerContext context = new ODataMaterializerContext(responseInfo, annotationsCache); - EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context, context); - this.materializer = new ODataEntriesEntityMaterializer(entries, context, entityTrackingAdapter, qc, materializerType, null, format); + ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, annotationsCache); + EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context, materializerContext); + this.materializer = new ODataEntriesEntityMaterializer(entries, materializerContext, entityTrackingAdapter, qc, materializerType, null, format); } /// <summary> diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextNoTrackingStreamsTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextNoTrackingStreamsTests.cs index 12cf8e1486..9dc9718aad 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextNoTrackingStreamsTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextNoTrackingStreamsTests.cs @@ -234,7 +234,7 @@ public void TestAddingNewItemsBehaviourShouldBeUnAltered() Assert.NotNull(NonTrackingContext.GetEntityDescriptor(user)); Assert.NotNull(DefaultTrackingContext.GetEntityDescriptor(user)); - SaveContextChanges(new DataServiceContext[] { DefaultTrackingContext, NonTrackingContext }); + SaveContextChanges(new DataServiceContext[] { DefaultTrackingContext });//, NonTrackingContext }); Assert.Single(DefaultTrackingContext.Entities); Assert.Single(NonTrackingContext.Entities); } diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs index 8652bd87a5..07e8c782d3 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs @@ -50,7 +50,7 @@ public void ComplexWithPrimitiveValueShouldMaterialize() [Fact] public void ApplyNonExistantPropertyWithIgnoreMissingPropertiesShouldNotError() { - TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()) { UndeclaredPropertyBehavior = DSClient.UndeclaredPropertyBehavior.Support }; + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { UndeclaredPropertyBehavior = DSClient.UndeclaredPropertyBehavior.Support }; CollectionValueMaterializationPolicyTests.Point point = new CollectionValueMaterializationPolicyTests.Point(); ODataProperty property = new ODataProperty() { Name = "Z", Value = 10 }; this.CreateEntryMaterializationPolicy(context) @@ -60,7 +60,7 @@ public void ApplyNonExistantPropertyWithIgnoreMissingPropertiesShouldNotError() [Fact] public void ApplyNullOnCollectionPropertyShouldError() { - TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); ODataProperty property = new ODataProperty() { Name = "Strings", Value = null }; @@ -71,7 +71,7 @@ public void ApplyNullOnCollectionPropertyShouldError() [Fact] public void ApplyStringValueForCollectionPropertyShouldError() { - TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); ODataProperty property = new ODataProperty() { Name = "Strings", Value = "foo" }; @@ -83,7 +83,7 @@ public void ApplyStringValueForCollectionPropertyShouldError() public void MaterializeDerivedComplexForBaseComplexTypeProperty() { var annotationsCache = new MaterializerAnnotationsCache(); - TestMaterializerContext context = new TestMaterializerContext(annotationsCache); + TestMaterializerContext materializerContext = new TestMaterializerContext(annotationsCache); //In a true client, a TypeResolver will be used to resolve derived property type. context.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => @@ -110,7 +110,7 @@ public void MaterializeDerivedComplexForBaseComplexTypeProperty() [Fact] public void ApplyDerivedComplexForBaseComplexTypeProperty() { - TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); context.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { @@ -141,7 +141,7 @@ public void ApplyDerivedComplexForBaseComplexTypeProperty() [Fact] public void ApplyODataCollectionValueToNonNullExistingCollectionProperty() { - TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); complexInstance.Strings.Add("ShouldBeCleared"); @@ -157,7 +157,7 @@ public void ApplyODataCollectionValueToNonNullExistingCollectionProperty() [Fact] public void ApplyODataCollectionValueToNullCollectionProperty() { - TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); complexInstance.Strings = null; ODataProperty property = new ODataProperty() { Name = "Strings", Value = new ODataCollectionValue() { Items = new string[] { "foo" }, TypeName = typeof(ComplexTypeWithPrimitiveCollection).FullName } }; @@ -172,7 +172,7 @@ public void ValueShouldBeAppliedRegardlessIfPropertyStartsNullOrNot() { foreach (var startingPropertyState in new ChildComplexType[] { null, new ChildComplexType() }) { - TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithChildComplexType complexInstance = new ComplexTypeWithChildComplexType(); complexInstance.InnerComplexProperty = startingPropertyState; var innerEntry = new ODataResource() { Properties = new ODataProperty[] { new ODataProperty() { Name = "Prop", Value = 1 } } }; @@ -184,7 +184,7 @@ public void ValueShouldBeAppliedRegardlessIfPropertyStartsNullOrNot() [Fact] public void NullValueShouldBeAppliedToSubComplexValueProperty() { - TestMaterializerContext context = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); ComplexTypeWithChildComplexType complexInstance = new ComplexTypeWithChildComplexType(); complexInstance.InnerComplexProperty = new ChildComplexType(); @@ -192,7 +192,7 @@ public void NullValueShouldBeAppliedToSubComplexValueProperty() Assert.Null(complexInstance.InnerComplexProperty); } - private void ApplyInnerProperty(ODataResource innerResource, ComplexTypeWithChildComplexType parentInstance, TestMaterializerContext context = null) + private void ApplyInnerProperty(ODataResource innerResource, ComplexTypeWithChildComplexType parentInstance, TestMaterializerContext materializerContext = null) { context = context ?? new TestMaterializerContext(new MaterializerAnnotationsCache()); var resource = new ODataResource() { TypeName = "ComplexTypeWithChildComplexType", Properties = new ODataProperty[0] }; From f52c0a0d4992a627058c64f66e369c84b33a3370 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Sat, 17 Sep 2022 19:41:10 +0300 Subject: [PATCH 12/16] Remove whitespace --- src/Microsoft.OData.Client/DataServiceRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OData.Client/DataServiceRequest.cs b/src/Microsoft.OData.Client/DataServiceRequest.cs index d68b3105de..8691177c80 100644 --- a/src/Microsoft.OData.Client/DataServiceRequest.cs +++ b/src/Microsoft.OData.Client/DataServiceRequest.cs @@ -79,7 +79,7 @@ internal static MaterializeAtom Materialize( { return MaterializeAtom.EmptyResults; } - + return new MaterializeAtom(responseInfo, queryComponents, plan, message, expectedPayloadKind, annotationsCache); } From a08b019768c9d599c80bb29cc291e0eeefbd96b4 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Sat, 17 Sep 2022 19:48:32 +0300 Subject: [PATCH 13/16] Use materializerContext parameter name instead of context --- .../CollectionValueMaterializationPolicy.cs | 6 +++--- .../EntryValueMaterializationPolicy.cs | 6 +++--- .../EnumValueMaterializationPolicy.cs | 14 +++++++------- .../MaterializerContextExtensions.cs | 14 ++++++-------- .../ODataMessageReaderMaterializer.cs | 6 +++--- .../PrimitiveValueMaterializationPolicy.cs | 12 ++++++------ 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/Microsoft.OData.Client/Materialization/CollectionValueMaterializationPolicy.cs b/src/Microsoft.OData.Client/Materialization/CollectionValueMaterializationPolicy.cs index dd559d54ce..6e70cf9d51 100644 --- a/src/Microsoft.OData.Client/Materialization/CollectionValueMaterializationPolicy.cs +++ b/src/Microsoft.OData.Client/Materialization/CollectionValueMaterializationPolicy.cs @@ -34,11 +34,11 @@ internal class CollectionValueMaterializationPolicy : MaterializationPolicy /// <summary> /// Initializes a new instance of the <see cref="CollectionValueMaterializationPolicy" /> class. /// </summary> - /// <param name="context">The context.</param> + /// <param name="materializerContext">The context.</param> /// <param name="primitivePolicy">The primitive policy.</param> - internal CollectionValueMaterializationPolicy(IODataMaterializerContext context, PrimitiveValueMaterializationPolicy primitivePolicy) + internal CollectionValueMaterializationPolicy(IODataMaterializerContext materializerContext, PrimitiveValueMaterializationPolicy primitivePolicy) { - this.materializerContext = context; + this.materializerContext = materializerContext; this.primitiveValueMaterializationPolicy = primitivePolicy; } diff --git a/src/Microsoft.OData.Client/Materialization/EntryValueMaterializationPolicy.cs b/src/Microsoft.OData.Client/Materialization/EntryValueMaterializationPolicy.cs index 1d58a28020..642cfff103 100644 --- a/src/Microsoft.OData.Client/Materialization/EntryValueMaterializationPolicy.cs +++ b/src/Microsoft.OData.Client/Materialization/EntryValueMaterializationPolicy.cs @@ -30,16 +30,16 @@ internal class EntryValueMaterializationPolicy : StructuralValueMaterializationP /// <summary> /// Initializes a new instance of the <see cref="EntryValueMaterializationPolicy" /> class. /// </summary> - /// <param name="context">The context.</param> + /// <param name="materializerContext">The context.</param> /// <param name="entityTrackingAdapter">The entity tracking adapter.</param> /// <param name="lazyPrimitivePropertyConverter">The lazy primitive property converter.</param> /// <param name="nextLinkTable">The next link table.</param> internal EntryValueMaterializationPolicy( - IODataMaterializerContext context, + IODataMaterializerContext materializerContext, EntityTrackingAdapter entityTrackingAdapter, DSClient.SimpleLazy<PrimitivePropertyConverter> lazyPrimitivePropertyConverter, Dictionary<IEnumerable, DataServiceQueryContinuation> nextLinkTable) - : base(context, lazyPrimitivePropertyConverter) + : base(materializerContext, lazyPrimitivePropertyConverter) { this.nextLinkTable = nextLinkTable; this.EntityTrackingAdapter = entityTrackingAdapter; diff --git a/src/Microsoft.OData.Client/Materialization/EnumValueMaterializationPolicy.cs b/src/Microsoft.OData.Client/Materialization/EnumValueMaterializationPolicy.cs index e5a6f5e314..a0c6562685 100644 --- a/src/Microsoft.OData.Client/Materialization/EnumValueMaterializationPolicy.cs +++ b/src/Microsoft.OData.Client/Materialization/EnumValueMaterializationPolicy.cs @@ -18,15 +18,15 @@ namespace Microsoft.OData.Client.Materialization internal class EnumValueMaterializationPolicy { /// <summary> MaterializerContext used to resolve types for materialization. </summary> - private readonly IODataMaterializerContext context; + private readonly IODataMaterializerContext materializerContext; /// <summary> /// Initializes a new instance of the <see cref="EnumValueMaterializationPolicy" /> class. /// </summary> - /// <param name="context">The context.</param> - internal EnumValueMaterializationPolicy(IODataMaterializerContext context) + /// <param name="materializerContext">The materializer context.</param> + internal EnumValueMaterializationPolicy(IODataMaterializerContext materializerContext) { - this.context = context; + this.materializerContext = materializerContext; } /// <summary> @@ -40,9 +40,9 @@ public object MaterializeEnumTypeProperty(Type valueType, ODataProperty property object materializedValue = null; ODataEnumValue value = property.Value as ODataEnumValue; this.MaterializeODataEnumValue(valueType, value.TypeName, value.Value, () => "TODO: Is this reachable?", out materializedValue); - if (!property.HasMaterializedValue(this.context)) + if (!property.HasMaterializedValue(this.materializerContext)) { - property.SetMaterializedValue(materializedValue, this.context); + property.SetMaterializedValue(materializedValue, this.materializerContext); } return materializedValue; @@ -102,7 +102,7 @@ private void MaterializeODataEnumValue(Type type, string wireTypeName, string en { Debug.Assert(type != null, "type != null"); Type underlyingType = Nullable.GetUnderlyingType(type) ?? type; - ClientTypeAnnotation elementTypeAnnotation = this.context.ResolveTypeForMaterialization(underlyingType, wireTypeName); + ClientTypeAnnotation elementTypeAnnotation = this.materializerContext.ResolveTypeForMaterialization(underlyingType, wireTypeName); Debug.Assert(elementTypeAnnotation != null, "elementTypeAnnotation != null"); // TODO: Find better way to parse Enum diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs b/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs index 81ed8d029e..7921896f2e 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs @@ -10,29 +10,27 @@ internal static class MaterializerContextExtensions { /// <summary> /// Associates the specified <paramref name="value"/> with the specified - /// <paramref name="annotatable"/> to store metadata used for materialization. /// </summary> /// <typeparam name="T">The type of the value.</typeparam> - /// <param name="context">The materializer context.</param> + /// <param name="materializerContext">The materializer context.</param> /// <param name="annotatable">The item to annotate.</param> /// <param name="value">The annotation value.</param> - public static void SetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable, T value) where T : class + public static void SetAnnotation<T>(this IODataMaterializerContext materializerContext, ODataAnnotatable annotatable, T value) where T : class { - context.AnnotationsCache.SetAnnotation(annotatable, value); + materializerContext.AnnotationsCache.SetAnnotation(annotatable, value); } /// <summary> /// Retrieves the value associated with the specified <paramref name="annotatable"/>. - /// </summary> /// <typeparam name="T">The expected type of the annotation value.</typeparam> - /// <param name="context">The materializer context.</param> + /// <param name="materializerContext">The materializer context.</param> /// <param name="annotatable">The item for which to retrieve the annotation.</param> /// <returns>The annotation value associated with the <paramref name="annotatable"/> if it exists, or null otherwise.</returns> - public static T GetAnnotation<T>(this IODataMaterializerContext context, ODataAnnotatable annotatable) where T : class + public static T GetAnnotation<T>(this IODataMaterializerContext materializerContext, ODataAnnotatable annotatable) where T : class { - return context.AnnotationsCache.GetAnnotation<T>(annotatable); + return materializerContext.AnnotationsCache.GetAnnotation<T>(annotatable); } } } diff --git a/src/Microsoft.OData.Client/Materialization/ODataMessageReaderMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataMessageReaderMaterializer.cs index 61057d044a..cd3b21151d 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataMessageReaderMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataMessageReaderMaterializer.cs @@ -32,11 +32,11 @@ internal abstract class ODataMessageReaderMaterializer : ODataMaterializer /// Initializes a new instance of the <see cref="ODataMessageReaderMaterializer"/> class. /// </summary> /// <param name="reader">The reader.</param> - /// <param name="context">The materializer context.</param> + /// <param name="materializerContext">The materializer context.</param> /// <param name="expectedType">The expected type.</param> /// <param name="singleResult">The single result.</param> - public ODataMessageReaderMaterializer(ODataMessageReader reader, IODataMaterializerContext context, Type expectedType, bool? singleResult) - : base(context, expectedType) + public ODataMessageReaderMaterializer(ODataMessageReader reader, IODataMaterializerContext materializerContext, Type expectedType, bool? singleResult) + : base(materializerContext, expectedType) { this.messageReader = reader; this.SingleResult = singleResult; diff --git a/src/Microsoft.OData.Client/Materialization/PrimitiveValueMaterializationPolicy.cs b/src/Microsoft.OData.Client/Materialization/PrimitiveValueMaterializationPolicy.cs index 034f3b3bb5..707452cca0 100644 --- a/src/Microsoft.OData.Client/Materialization/PrimitiveValueMaterializationPolicy.cs +++ b/src/Microsoft.OData.Client/Materialization/PrimitiveValueMaterializationPolicy.cs @@ -17,7 +17,7 @@ namespace Microsoft.OData.Client.Materialization internal class PrimitiveValueMaterializationPolicy { /// <summary> MaterializerContext used to resolve types for materialization. </summary> - private readonly IODataMaterializerContext context; + private readonly IODataMaterializerContext materializerContext; /// <summary> /// primitive property converter used to convert the property have the value has been materialized. </summary> @@ -26,11 +26,11 @@ internal class PrimitiveValueMaterializationPolicy /// <summary> /// Initializes a new instance of the <see cref="PrimitiveValueMaterializationPolicy" /> class. /// </summary> - /// <param name="context">The context.</param> + /// <param name="materializerContext">The context.</param> /// <param name="lazyPrimitivePropertyConverter">The lazy primitive property converter.</param> - internal PrimitiveValueMaterializationPolicy(IODataMaterializerContext context, SimpleLazy<PrimitivePropertyConverter> lazyPrimitivePropertyConverter) + internal PrimitiveValueMaterializationPolicy(IODataMaterializerContext materializerContext, SimpleLazy<PrimitivePropertyConverter> lazyPrimitivePropertyConverter) { - this.context = context; + this.materializerContext = materializerContext; this.lazyPrimitivePropertyConverter = lazyPrimitivePropertyConverter; } @@ -92,7 +92,7 @@ private void MaterializePrimitiveDataValue(Type type, string wireTypeName, objec bool knownType = PrimitiveType.TryGetPrimitiveType(underlyingType, out ptype); if (!knownType) { - nestedElementType = this.context.ResolveTypeForMaterialization(type, wireTypeName); + nestedElementType = this.materializerContext.ResolveTypeForMaterialization(type, wireTypeName); Debug.Assert(nestedElementType != null, "nestedElementType != null -- otherwise ReadTypeAttribute (or someone!) should throw"); knownType = PrimitiveType.TryGetPrimitiveType(nestedElementType.ElementType, out ptype); } @@ -112,7 +112,7 @@ private void MaterializePrimitiveDataValue(Type type, string wireTypeName, objec { ODataUntypedValue untypedVal = value as ODataUntypedValue; if ((untypedVal != null) - && this.context.UndeclaredPropertyBehavior == UndeclaredPropertyBehavior.Support) + && this.materializerContext.UndeclaredPropertyBehavior == UndeclaredPropertyBehavior.Support) { value = CommonUtil.ParseJsonToPrimitiveValue(untypedVal.RawValue); } From 3c9745f3a97aa7298d1c43831688d21699c6430c Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Sat, 17 Sep 2022 20:17:14 +0300 Subject: [PATCH 14/16] Fix build errors --- ...ializationPolicyForComplexResourceTests.cs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs index 07e8c782d3..3ab8301b14 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs @@ -53,8 +53,8 @@ public void ApplyNonExistantPropertyWithIgnoreMissingPropertiesShouldNotError() TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { UndeclaredPropertyBehavior = DSClient.UndeclaredPropertyBehavior.Support }; CollectionValueMaterializationPolicyTests.Point point = new CollectionValueMaterializationPolicyTests.Point(); ODataProperty property = new ODataProperty() { Name = "Z", Value = 10 }; - this.CreateEntryMaterializationPolicy(context) - .ApplyDataValue(context.ResolveTypeForMaterialization(typeof(CollectionValueMaterializationPolicyTests.Point), null), property, point); + this.CreateEntryMaterializationPolicy(materializerContext) + .ApplyDataValue(materializerContext.ResolveTypeForMaterialization(typeof(CollectionValueMaterializationPolicyTests.Point), null), property, point); } [Fact] @@ -64,7 +64,7 @@ public void ApplyNullOnCollectionPropertyShouldError() ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); ODataProperty property = new ODataProperty() { Name = "Strings", Value = null }; - Action test = () => this.CreateEntryMaterializationPolicy(context).ApplyDataValue(context.ResolveTypeForMaterialization(typeof(ComplexTypeWithPrimitiveCollection), null), property, complexInstance); + Action test = () => this.CreateEntryMaterializationPolicy(materializerContext).ApplyDataValue(materializerContext.ResolveTypeForMaterialization(typeof(ComplexTypeWithPrimitiveCollection), null), property, complexInstance); test.ShouldThrow<InvalidOperationException>().WithMessage(DSClient.Strings.Collection_NullCollectionNotSupported(property.Name)); } @@ -75,7 +75,7 @@ public void ApplyStringValueForCollectionPropertyShouldError() ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); ODataProperty property = new ODataProperty() { Name = "Strings", Value = "foo" }; - Action test = () => this.CreateEntryMaterializationPolicy(context).ApplyDataValue(context.ResolveTypeForMaterialization(typeof(ComplexTypeWithPrimitiveCollection), null), property, complexInstance); + Action test = () => this.CreateEntryMaterializationPolicy(materializerContext).ApplyDataValue(materializerContext.ResolveTypeForMaterialization(typeof(ComplexTypeWithPrimitiveCollection), null), property, complexInstance); test.ShouldThrow<InvalidOperationException>().WithMessage(DSClient.Strings.Deserialize_MixedTextWithComment); } @@ -86,10 +86,10 @@ public void MaterializeDerivedComplexForBaseComplexTypeProperty() TestMaterializerContext materializerContext = new TestMaterializerContext(annotationsCache); //In a true client, a TypeResolver will be used to resolve derived property type. - context.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => + materializerContext.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { - var edmType = context.Model.GetOrCreateEdmType(typeof(DerivedComplexType)); - return new ClientTypeAnnotation(edmType, typeof(DerivedComplexType), "DerivedComplexType", context.Model); + var edmType = materializerContext.Model.GetOrCreateEdmType(typeof(DerivedComplexType)); + return new ClientTypeAnnotation(edmType, typeof(DerivedComplexType), "DerivedComplexType", materializerContext.Model); }; var derivedResource = new ODataResource() @@ -99,8 +99,8 @@ public void MaterializeDerivedComplexForBaseComplexTypeProperty() }; var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); - var materializerEntry = MaterializerEntry.CreateEntry(derivedResource, ODataFormat.Json, true, clientEdmModel, context); - this.CreateEntryMaterializationPolicy(context).Materialize(materializerEntry, typeof(ChildComplexType), false); + var materializerEntry = MaterializerEntry.CreateEntry(derivedResource, ODataFormat.Json, true, clientEdmModel, materializerContext); + this.CreateEntryMaterializationPolicy(materializerContext).Materialize(materializerEntry, typeof(ChildComplexType), false); var derived = materializerEntry.ResolvedObject as DerivedComplexType; Assert.NotNull(derived); @@ -112,17 +112,17 @@ public void ApplyDerivedComplexForBaseComplexTypeProperty() { TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); - context.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => + materializerContext.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { if (name == "DerivedComplexType") { - var edmType = context.Model.GetOrCreateEdmType(typeof(DerivedComplexType)); - return new ClientTypeAnnotation(edmType, typeof(DerivedComplexType), "DerivedComplexType", context.Model); + var edmType = materializerContext.Model.GetOrCreateEdmType(typeof(DerivedComplexType)); + return new ClientTypeAnnotation(edmType, typeof(DerivedComplexType), "DerivedComplexType", materializerContext.Model); } else { - var edmType = context.Model.GetOrCreateEdmType(typeof(ComplexTypeWithChildComplexType)); - return new ClientTypeAnnotation(edmType, typeof(ComplexTypeWithChildComplexType), "ComplexTypeWithChildComplexType", context.Model); + var edmType = materializerContext.Model.GetOrCreateEdmType(typeof(ComplexTypeWithChildComplexType)); + return new ClientTypeAnnotation(edmType, typeof(ComplexTypeWithChildComplexType), "ComplexTypeWithChildComplexType", materializerContext.Model); } }; ComplexTypeWithChildComplexType complexInstance = new ComplexTypeWithChildComplexType(); @@ -133,7 +133,7 @@ public void ApplyDerivedComplexForBaseComplexTypeProperty() TypeName = "DerivedComplexType", Properties = new ODataProperty[] { new ODataProperty { Name = "DerivedProp", Value = 2 } } }; - this.ApplyInnerProperty(innerResource, complexInstance, context); + this.ApplyInnerProperty(innerResource, complexInstance, materializerContext); Assert.Equal(2, (complexInstance.InnerComplexProperty as DerivedComplexType).DerivedProp); } @@ -147,9 +147,9 @@ public void ApplyODataCollectionValueToNonNullExistingCollectionProperty() complexInstance.Strings.Add("ShouldBeCleared"); ODataProperty property = new ODataProperty() { Name = "Strings", Value = new ODataCollectionValue() }; - this.CreateEntryMaterializationPolicy(context) + this.CreateEntryMaterializationPolicy(materializerContext) .ApplyDataValue( - context.ResolveTypeForMaterialization(typeof(ComplexTypeWithPrimitiveCollection), null), + materializerContext.ResolveTypeForMaterialization(typeof(ComplexTypeWithPrimitiveCollection), null), property, complexInstance); complexInstance.Strings.Should().HaveCount(0); } @@ -162,7 +162,7 @@ public void ApplyODataCollectionValueToNullCollectionProperty() complexInstance.Strings = null; ODataProperty property = new ODataProperty() { Name = "Strings", Value = new ODataCollectionValue() { Items = new string[] { "foo" }, TypeName = typeof(ComplexTypeWithPrimitiveCollection).FullName } }; - this.CreateEntryMaterializationPolicy(context).ApplyDataValue(context.ResolveTypeForMaterialization(typeof(ComplexTypeWithPrimitiveCollection), null), property, complexInstance); + this.CreateEntryMaterializationPolicy(materializerContext).ApplyDataValue(materializerContext.ResolveTypeForMaterialization(typeof(ComplexTypeWithPrimitiveCollection), null), property, complexInstance); complexInstance.Strings.Should().HaveCount(1); complexInstance.Strings[0].Should().Be("foo"); } @@ -194,28 +194,28 @@ public void NullValueShouldBeAppliedToSubComplexValueProperty() private void ApplyInnerProperty(ODataResource innerResource, ComplexTypeWithChildComplexType parentInstance, TestMaterializerContext materializerContext = null) { - context = context ?? new TestMaterializerContext(new MaterializerAnnotationsCache()); + materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerAnnotationsCache()); var resource = new ODataResource() { TypeName = "ComplexTypeWithChildComplexType", Properties = new ODataProperty[0] }; var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); - var materializerEntry = MaterializerEntry.CreateEntry(resource, ODataFormat.Json, false, clientEdmModel, context); + var materializerEntry = MaterializerEntry.CreateEntry(resource, ODataFormat.Json, false, clientEdmModel, materializerContext); materializerEntry.ResolvedObject = parentInstance; ODataNestedResourceInfo innerComplexP = new ODataNestedResourceInfo() { Name = "InnerComplexProperty", IsCollection = false }; MaterializerEntry innerMaterializerEntry; if (innerResource != null) { - innerMaterializerEntry = MaterializerEntry.CreateEntry(innerResource, ODataFormat.Json, true, clientEdmModel, context); + innerMaterializerEntry = MaterializerEntry.CreateEntry(innerResource, ODataFormat.Json, true, clientEdmModel, materializerContext); } else { innerMaterializerEntry = MaterializerEntry.CreateEmpty(); } - MaterializerNavigationLink.CreateLink(innerComplexP, innerMaterializerEntry, context); + MaterializerNavigationLink.CreateLink(innerComplexP, innerMaterializerEntry, materializerContext); materializerEntry.AddNestedResourceInfo(innerComplexP); - var policy = this.CreateEntryMaterializationPolicy(context); + var policy = this.CreateEntryMaterializationPolicy(materializerContext); policy.EntityTrackingAdapter.TargetInstance = parentInstance; policy.Materialize(materializerEntry, typeof(ComplexTypeWithChildComplexType), true); } From b208a03938408d21d8b3d4bf62cbb51b362f5ea9 Mon Sep 17 00:00:00 2001 From: Clement Habinshuti <clhabins@microsoft.com> Date: Sat, 17 Sep 2022 22:36:38 +0300 Subject: [PATCH 15/16] Rename MaterializerAnnotationsCache to MaterializerCache --- src/Microsoft.OData.Client/BaseAsyncResult.cs | 4 +-- src/Microsoft.OData.Client/BatchSaveResult.cs | 4 +-- .../DataServiceRequest.cs | 6 ++-- .../IODataMaterializerContext.cs | 6 +++- ...notationsCache.cs => MaterializerCache.cs} | 5 ++-- .../MaterializerContextExtensions.cs | 4 +-- .../Materialization/ODataItemExtensions.cs | 2 +- .../Materialization/ODataMaterializer.cs | 5 ++-- .../ODataMaterializerContext.cs | 8 ++++-- .../MaterializeFromAtom.cs | 12 ++++---- src/Microsoft.OData.Client/QueryResult.cs | 2 +- src/Microsoft.OData.Client/SaveResult.cs | 4 +-- ...ataServiceContextNoTrackingStreamsTests.cs | 2 +- .../Tests/DataServiceRequestTests.cs | 4 +-- ...llectionValueMaterializationPolicyTests.cs | 10 +++---- ...ializationPolicyForComplexResourceTests.cs | 28 +++++++++---------- ...ntryValueMaterializationPolicyUnitTests.cs | 2 +- ...eedAndEntryMaterializerAdapterUnitTests.cs | 2 +- .../Materialization/MaterializerEntryTests.cs | 2 +- .../ODataEntityMaterializerUnitTests.cs | 2 +- ...ODataEntriesEntityMaterializerUnitTests.cs | 2 +- ...rimitiveValueMaterializationPolicyTests.cs | 2 +- .../TestMaterializerContext.cs | 6 ++-- .../Tests/T4/ODataT4CamelCaseTests.cs | 8 +++--- 24 files changed, 69 insertions(+), 63 deletions(-) rename src/Microsoft.OData.Client/Materialization/{MaterializerAnnotationsCache.cs => MaterializerCache.cs} (94%) diff --git a/src/Microsoft.OData.Client/BaseAsyncResult.cs b/src/Microsoft.OData.Client/BaseAsyncResult.cs index 323c792089..6d9140043b 100644 --- a/src/Microsoft.OData.Client/BaseAsyncResult.cs +++ b/src/Microsoft.OData.Client/BaseAsyncResult.cs @@ -28,9 +28,9 @@ internal abstract class BaseAsyncResult : IAsyncResult protected PerRequest perRequest; /// <summary> - /// Cache used to store temporary metadata for OData items during materialization. + /// Cache used to store temporary metadata used for materialization of OData items. /// </summary> - protected MaterializerAnnotationsCache annotationsCache = new MaterializerAnnotationsCache(); + protected MaterializerCache materializerCache = new MaterializerCache(); /// <summary> /// The int equivalent for true. diff --git a/src/Microsoft.OData.Client/BatchSaveResult.cs b/src/Microsoft.OData.Client/BatchSaveResult.cs index 6327a2b612..cd3a240a3c 100644 --- a/src/Microsoft.OData.Client/BatchSaveResult.cs +++ b/src/Microsoft.OData.Client/BatchSaveResult.cs @@ -254,7 +254,7 @@ protected override MaterializeAtom GetMaterializer(EntityDescriptor entityDescri /*projectionPlan*/ null, this.currentOperationResponse.CreateResponseMessage(), ODataPayloadKind.Resource, - this.annotationsCache); + this.materializerCache); } /// <summary> @@ -690,7 +690,7 @@ private IEnumerable<OperationResponse> HandleBatchResponse(ODataBatchReader batc this.currentOperationResponse.Headers.GetHeader(XmlConstants.HttpContentType), this.currentOperationResponse.CreateResponseMessage(), query.PayloadKind, - this.annotationsCache); + this.materializerCache); qresponse = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, materializer); } } diff --git a/src/Microsoft.OData.Client/DataServiceRequest.cs b/src/Microsoft.OData.Client/DataServiceRequest.cs index 8691177c80..e24e5978a0 100644 --- a/src/Microsoft.OData.Client/DataServiceRequest.cs +++ b/src/Microsoft.OData.Client/DataServiceRequest.cs @@ -60,7 +60,7 @@ internal ODataPayloadKind PayloadKind /// <param name="contentType">contentType</param> /// <param name="message">the message</param> /// <param name="expectedPayloadKind">expected payload kind.</param> - /// <param name="annotationsCache">The annotations cache used to store temporary metadata used for materialization of OData items.</param> + /// <param name="materializerCache">Cache used to store temporary metadata used for materialization of OData items.</param> /// <returns>atom materializer</returns> internal static MaterializeAtom Materialize( ResponseInfo responseInfo, @@ -69,7 +69,7 @@ internal static MaterializeAtom Materialize( string contentType, IODataResponseMessage message, ODataPayloadKind expectedPayloadKind, - MaterializerAnnotationsCache annotationsCache) + MaterializerCache materializerCache) { Debug.Assert(queryComponents != null, "querycomponents"); Debug.Assert(message != null, "message"); @@ -80,7 +80,7 @@ internal static MaterializeAtom Materialize( return MaterializeAtom.EmptyResults; } - return new MaterializeAtom(responseInfo, queryComponents, plan, message, expectedPayloadKind, annotationsCache); + return new MaterializeAtom(responseInfo, queryComponents, plan, message, expectedPayloadKind, materializerCache); } /// <summary> diff --git a/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs b/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs index edb4c7e90d..9f435bce90 100644 --- a/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs +++ b/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs @@ -53,6 +53,10 @@ internal interface IODataMaterializerContext /// <returns>The resolved EDM type to provide to ODataLib.</returns> IEdmType ResolveExpectedTypeForReading(Type clientClrType); - MaterializerAnnotationsCache AnnotationsCache { get; } + /// <summary> + /// Used to store temporary metadata used to converter deserialized + /// OData items into CLR objects. + /// </summary> + MaterializerCache MaterializerCache { get; } } } diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs b/src/Microsoft.OData.Client/Materialization/MaterializerCache.cs similarity index 94% rename from src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs rename to src/Microsoft.OData.Client/Materialization/MaterializerCache.cs index 8911320329..fb8ddfa117 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerAnnotationsCache.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerCache.cs @@ -1,5 +1,5 @@ //--------------------------------------------------------------------- -// <copyright file="MaterializerAnnotationsCache.cs" company="Microsoft"> +// <copyright file="MaterializerCache.cs" company="Microsoft"> // Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. // </copyright> //--------------------------------------------------------------------- @@ -12,7 +12,6 @@ namespace Microsoft.OData.Client.Materialization /// <summary> /// A cache used to store temporary materialization metadata /// for deserialized <see cref="ODataItem"/>s during materialization of response payloads - /// into client CLR objects. Keys are identified using reference equality. /// </summary> /// <remarks> @@ -26,7 +25,7 @@ namespace Microsoft.OData.Client.Materialization /// If the requirements change such that we need to allow updating the value of a key to a different type, /// then the cache should be redesigned. /// </remarks> - internal sealed class MaterializerAnnotationsCache + internal sealed class MaterializerCache { private readonly Dictionary<ODataAnnotatable, object> cache = new Dictionary<ODataAnnotatable, object>(ReferenceEqualityComparer<ODataAnnotatable>.Instance); diff --git a/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs b/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs index 7921896f2e..31ad376af9 100644 --- a/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs +++ b/src/Microsoft.OData.Client/Materialization/MaterializerContextExtensions.cs @@ -18,7 +18,7 @@ internal static class MaterializerContextExtensions /// <param name="value">The annotation value.</param> public static void SetAnnotation<T>(this IODataMaterializerContext materializerContext, ODataAnnotatable annotatable, T value) where T : class { - materializerContext.AnnotationsCache.SetAnnotation(annotatable, value); + materializerContext.MaterializerCache.SetAnnotation(annotatable, value); } /// <summary> @@ -30,7 +30,7 @@ public static void SetAnnotation<T>(this IODataMaterializerContext materializerC /// <returns>The annotation value associated with the <paramref name="annotatable"/> if it exists, or null otherwise.</returns> public static T GetAnnotation<T>(this IODataMaterializerContext materializerContext, ODataAnnotatable annotatable) where T : class { - return materializerContext.AnnotationsCache.GetAnnotation<T>(annotatable); + return materializerContext.MaterializerCache.GetAnnotation<T>(annotatable); } } } diff --git a/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs b/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs index a48beae608..309211ea46 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataItemExtensions.cs @@ -58,7 +58,7 @@ public static void SetMaterializedValue(this ODataProperty property, object mate /// <returns>The materialized value</returns> private static object GetMaterializedValueCore(ODataAnnotatable annotatableObject, IODataMaterializerContext materializerContext) { - MaterializerPropertyValue value = materializerContext.AnnotationsCache.GetAnnotation<MaterializerPropertyValue>(annotatableObject); + MaterializerPropertyValue value = materializerContext.MaterializerCache.GetAnnotation<MaterializerPropertyValue>(annotatableObject); Debug.Assert(value != null, "MaterializedValue not set"); return value.Value; } diff --git a/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs b/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs index 91f2f98d12..f1bbf7e73d 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataMaterializer.cs @@ -184,6 +184,7 @@ protected PrimitiveValueMaterializationPolicy PrimitiveValueMaterializier /// <param name="queryComponents">The query components for the request.</param> /// <param name="plan">The projection plan.</param> /// <param name="payloadKind">expected payload kind.</param> + /// <param name="materializerCache">The materializer cache.</param> /// <returns>A materializer specialized for the given response.</returns> public static ODataMaterializer CreateMaterializerForMessage( IODataResponseMessage responseMessage, @@ -192,7 +193,7 @@ public static ODataMaterializer CreateMaterializerForMessage( QueryComponents queryComponents, ProjectionPlan plan, ODataPayloadKind payloadKind, - MaterializerAnnotationsCache annotationsCache) + MaterializerCache materializerCache) { ODataMessageReader messageReader = CreateODataMessageReader(responseMessage, responseInfo, ref payloadKind); @@ -201,7 +202,7 @@ public static ODataMaterializer CreateMaterializerForMessage( try { - ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, annotationsCache); + ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, materializerCache); // Since in V1/V2, astoria client allowed Execute<object> and depended on the typeresolver or the wire type name // to get the clr type to materialize. Hence if we see the materializer type as object, we should set the edmtype diff --git a/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs b/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs index 923a221257..bf8b0ca290 100644 --- a/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs +++ b/src/Microsoft.OData.Client/Materialization/ODataMaterializerContext.cs @@ -19,10 +19,11 @@ internal class ODataMaterializerContext : IODataMaterializerContext /// Initializes a materializer context /// </summary> /// <param name="responseInfo">Response information used to initialize with the materializer</param> - internal ODataMaterializerContext(ResponseInfo responseInfo, MaterializerAnnotationsCache annotationsCache) + /// <param name="materializerCache">The materializer cache.</param> + internal ODataMaterializerContext(ResponseInfo responseInfo, MaterializerCache materializerCache) { this.ResponseInfo = responseInfo; - this.AnnotationsCache = annotationsCache; + this.MaterializerCache = materializerCache; } /// <summary> @@ -86,6 +87,7 @@ public IEdmType ResolveExpectedTypeForReading(Type expectedType) return this.ResponseInfo.TypeResolver.ResolveExpectedTypeForReading(expectedType); } - public MaterializerAnnotationsCache AnnotationsCache { get; private set; } + /// <inheritdoc/> + public MaterializerCache MaterializerCache { get; private set; } } } diff --git a/src/Microsoft.OData.Client/MaterializeFromAtom.cs b/src/Microsoft.OData.Client/MaterializeFromAtom.cs index aff289e1d3..80b057e17b 100644 --- a/src/Microsoft.OData.Client/MaterializeFromAtom.cs +++ b/src/Microsoft.OData.Client/MaterializeFromAtom.cs @@ -74,14 +74,14 @@ internal class MaterializeAtom : IDisposable, IEnumerable, IEnumerator /// <param name="plan">Projection plan (if compiled in an earlier query).</param> /// <param name="responseMessage">responseMessage</param> /// <param name="payloadKind">The kind of the payload to materialize.</param> - /// <param name="annotationsCache">The annotations cache used to store and retrieve temporary metadata used for materialization of OData items.</param> + /// <param name="materializerCache">Cache used to store temporary metadata used for materialization of OData items.</param> internal MaterializeAtom( ResponseInfo responseInfo, QueryComponents queryComponents, ProjectionPlan plan, IODataResponseMessage responseMessage, ODataPayloadKind payloadKind, - MaterializerAnnotationsCache annotationsCache) + MaterializerCache materializerCache) { Debug.Assert(queryComponents != null, "queryComponents != null"); @@ -93,7 +93,7 @@ internal MaterializeAtom( Type implementationType; Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType); - this.materializer = ODataMaterializer.CreateMaterializerForMessage(responseMessage, responseInfo, materializerType, queryComponents, plan, payloadKind, annotationsCache); + this.materializer = ODataMaterializer.CreateMaterializerForMessage(responseMessage, responseInfo, materializerType, queryComponents, plan, payloadKind, materializerCache); } /// <summary> @@ -103,8 +103,8 @@ internal MaterializeAtom( /// <param name="entries">entries that needs to be materialized.</param> /// <param name="elementType">result type.</param> /// <param name="format">The format of the response being materialized from.</param> - /// <param name="annotationsCache">The annotations cache used to store and retrieve temporary metadata used for materialization of OData items.</param> - internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> entries, Type elementType, ODataFormat format, MaterializerAnnotationsCache annotationsCache) + /// <param name="materializerCache">Cache used to store temporary metadata used for materialization of OData items.</param> + internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> entries, Type elementType, ODataFormat format, MaterializerCache materializerCache) { this.responseInfo = responseInfo; this.elementType = elementType; @@ -113,7 +113,7 @@ internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable<ODataResource> e Type implementationType; Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType); QueryComponents qc = new QueryComponents(null, Util.ODataVersionEmpty, elementType, null, null); - ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, annotationsCache); + ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, materializerCache); EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context, materializerContext); this.materializer = new ODataEntriesEntityMaterializer(entries, materializerContext, entityTrackingAdapter, qc, materializerType, null, format); } diff --git a/src/Microsoft.OData.Client/QueryResult.cs b/src/Microsoft.OData.Client/QueryResult.cs index 812da403b0..eb0d6a7ed0 100644 --- a/src/Microsoft.OData.Client/QueryResult.cs +++ b/src/Microsoft.OData.Client/QueryResult.cs @@ -708,7 +708,7 @@ private MaterializeAtom CreateMaterializer(ProjectionPlan plan, ODataPayloadKind this.ContentType, responseMessageWrapper, payloadKind, - this.annotationsCache); + this.materializerCache); } } } diff --git a/src/Microsoft.OData.Client/SaveResult.cs b/src/Microsoft.OData.Client/SaveResult.cs index 80ce34218f..a3c14f7165 100644 --- a/src/Microsoft.OData.Client/SaveResult.cs +++ b/src/Microsoft.OData.Client/SaveResult.cs @@ -360,7 +360,7 @@ protected override MaterializeAtom GetMaterializer(EntityDescriptor entityDescri { Debug.Assert(this.cachedResponse.Exception == null && this.cachedResponse.MaterializerEntry != null, "this.cachedResponse.Exception == null && this.cachedResponse.Entry != null"); ODataResource entry = this.cachedResponse.MaterializerEntry == null ? null : this.cachedResponse.MaterializerEntry.Entry; - return new MaterializeAtom(responseInfo, new[] { entry }, entityDescriptor.Entity.GetType(), this.cachedResponse.MaterializerEntry.Format, annotationsCache); + return new MaterializeAtom(responseInfo, new[] { entry }, entityDescriptor.Entity.GetType(), this.cachedResponse.MaterializerEntry.Format, this.materializerCache); } /// <summary> @@ -865,7 +865,7 @@ private void HandleOperationResponseData(IODataResponseMessage responseMsg, Stre responseMsg.StatusCode, () => responseStream); - ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, this.annotationsCache); + ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo, this.materializerCache); entry = ODataReaderEntityMaterializer.ParseSingleEntityPayload(responseMessageWrapper, responseInfo, entityDescriptor.Entity.GetType(), materializerContext); entityDescriptor.TransientEntityDescriptor = entry.EntityDescriptor; } diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextNoTrackingStreamsTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextNoTrackingStreamsTests.cs index 9dc9718aad..12cf8e1486 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextNoTrackingStreamsTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextNoTrackingStreamsTests.cs @@ -234,7 +234,7 @@ public void TestAddingNewItemsBehaviourShouldBeUnAltered() Assert.NotNull(NonTrackingContext.GetEntityDescriptor(user)); Assert.NotNull(DefaultTrackingContext.GetEntityDescriptor(user)); - SaveContextChanges(new DataServiceContext[] { DefaultTrackingContext });//, NonTrackingContext }); + SaveContextChanges(new DataServiceContext[] { DefaultTrackingContext, NonTrackingContext }); Assert.Single(DefaultTrackingContext.Entities); Assert.Single(NonTrackingContext.Entities); } diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/DataServiceRequestTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/DataServiceRequestTests.cs index 07fd87e2ee..27edd8fd1c 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/DataServiceRequestTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/DataServiceRequestTests.cs @@ -59,7 +59,7 @@ private void MaterializeTest(HttpStatusCode statusCode, ODataPayloadKind payload new HeaderCollection(), (int)statusCode, () => new MemoryStream()); - var annotationsCache = new MaterializerAnnotationsCache(); + var materializerCache = new MaterializerCache(); var materialize = DataServiceRequest.Materialize( responseInfo, queryComponents, @@ -67,7 +67,7 @@ private void MaterializeTest(HttpStatusCode statusCode, ODataPayloadKind payload "application/json", responseMessage, payloadKind, - annotationsCache); + materializerCache); Assert.Null(materialize.Context); Assert.Null(materialize.Current); var enumerable = materialize.Cast<object>(); diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/CollectionValueMaterializationPolicyTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/CollectionValueMaterializationPolicyTests.cs index e0e3c7fdb9..03539d18cc 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/CollectionValueMaterializationPolicyTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/CollectionValueMaterializationPolicyTests.cs @@ -130,7 +130,7 @@ public void AddingCollectionToComplexCollectionShouldFail() [Fact] public void DataServicCollectionOfTAsCollectionTypeShouldFailForPrimitiveOrComplexCollections() { - var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var testContext = new TestMaterializerContext(new MaterializerCache()); var edmType = testContext.Model.GetOrCreateEdmType(typeof(MyInfo)); var clientTypeAnnotation = new ClientTypeAnnotation(edmType, typeof(MyInfo), "MyInfo", testContext.Model); @@ -141,7 +141,7 @@ public void DataServicCollectionOfTAsCollectionTypeShouldFailForPrimitiveOrCompl [Fact] public void CreateCollectionInstanceShouldFailOnTypeWithNoParametersLessConstructors() { - var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var testContext = new TestMaterializerContext(new MaterializerCache()); var edmType = testContext.Model.GetOrCreateEdmType(typeof(ListWithNoEmptyConstructors)); var clientTypeAnnotation = new ClientTypeAnnotation(edmType, typeof(ListWithNoEmptyConstructors), "Points", testContext.Model); @@ -153,7 +153,7 @@ public void CreateCollectionInstanceShouldFailOnTypeWithNoParametersLessConstruc public void CreateCollectionPropertyInstanceShouldFailOnTypeWithNoParametersLessConstructors() { var odataProperty = new ODataProperty() { Name = "foo", Value = new ODataCollectionValue() { TypeName = "Points" } }; - var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var testContext = new TestMaterializerContext(new MaterializerCache()); testContext.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { var edmType = testContext.Model.GetOrCreateEdmType(typeof(ListWithNoEmptyConstructors)); @@ -167,7 +167,7 @@ public void CreateCollectionPropertyInstanceShouldFailOnTypeWithNoParametersLess [Fact] public void NonMissingMethodExceptionOnCreateInstanceShouldNotBeCaught() { - var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var testContext = new TestMaterializerContext(new MaterializerCache()); var edmType = testContext.Model.GetOrCreateEdmType(typeof(ListWithNoEmptyConstructors)); var clientTypeAnnotation = new ClientTypeAnnotation(edmType, typeof(ErrorThrowingList), "Points", testContext.Model); @@ -181,7 +181,7 @@ public void NonMissingMethodExceptionOnCreateInstanceShouldNotBeCaught() internal CollectionValueMaterializationPolicy CreateCollectionValueMaterializationPolicy() { - return CreateCollectionValueMaterializationPolicy(new TestMaterializerContext(new MaterializerAnnotationsCache())); + return CreateCollectionValueMaterializationPolicy(new TestMaterializerContext(new MaterializerCache())); } internal CollectionValueMaterializationPolicy CreateCollectionValueMaterializationPolicy(IODataMaterializerContext materializerContext) diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs index 3ab8301b14..c8a45c571b 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryMaterializationPolicyForComplexResourceTests.cs @@ -50,7 +50,7 @@ public void ComplexWithPrimitiveValueShouldMaterialize() [Fact] public void ApplyNonExistantPropertyWithIgnoreMissingPropertiesShouldNotError() { - TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { UndeclaredPropertyBehavior = DSClient.UndeclaredPropertyBehavior.Support }; + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerCache()) { UndeclaredPropertyBehavior = DSClient.UndeclaredPropertyBehavior.Support }; CollectionValueMaterializationPolicyTests.Point point = new CollectionValueMaterializationPolicyTests.Point(); ODataProperty property = new ODataProperty() { Name = "Z", Value = 10 }; this.CreateEntryMaterializationPolicy(materializerContext) @@ -60,7 +60,7 @@ public void ApplyNonExistantPropertyWithIgnoreMissingPropertiesShouldNotError() [Fact] public void ApplyNullOnCollectionPropertyShouldError() { - TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); ODataProperty property = new ODataProperty() { Name = "Strings", Value = null }; @@ -71,7 +71,7 @@ public void ApplyNullOnCollectionPropertyShouldError() [Fact] public void ApplyStringValueForCollectionPropertyShouldError() { - TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); ODataProperty property = new ODataProperty() { Name = "Strings", Value = "foo" }; @@ -82,8 +82,8 @@ public void ApplyStringValueForCollectionPropertyShouldError() [Fact] public void MaterializeDerivedComplexForBaseComplexTypeProperty() { - var annotationsCache = new MaterializerAnnotationsCache(); - TestMaterializerContext materializerContext = new TestMaterializerContext(annotationsCache); + var materializerCache = new MaterializerCache(); + TestMaterializerContext materializerContext = new TestMaterializerContext(materializerCache); //In a true client, a TypeResolver will be used to resolve derived property type. materializerContext.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => @@ -110,7 +110,7 @@ public void MaterializeDerivedComplexForBaseComplexTypeProperty() [Fact] public void ApplyDerivedComplexForBaseComplexTypeProperty() { - TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerCache()); materializerContext.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { @@ -141,7 +141,7 @@ public void ApplyDerivedComplexForBaseComplexTypeProperty() [Fact] public void ApplyODataCollectionValueToNonNullExistingCollectionProperty() { - TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); complexInstance.Strings.Add("ShouldBeCleared"); @@ -157,7 +157,7 @@ public void ApplyODataCollectionValueToNonNullExistingCollectionProperty() [Fact] public void ApplyODataCollectionValueToNullCollectionProperty() { - TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerCache()); ComplexTypeWithPrimitiveCollection complexInstance = new ComplexTypeWithPrimitiveCollection(); complexInstance.Strings = null; ODataProperty property = new ODataProperty() { Name = "Strings", Value = new ODataCollectionValue() { Items = new string[] { "foo" }, TypeName = typeof(ComplexTypeWithPrimitiveCollection).FullName } }; @@ -172,7 +172,7 @@ public void ValueShouldBeAppliedRegardlessIfPropertyStartsNullOrNot() { foreach (var startingPropertyState in new ChildComplexType[] { null, new ChildComplexType() }) { - TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerCache()); ComplexTypeWithChildComplexType complexInstance = new ComplexTypeWithChildComplexType(); complexInstance.InnerComplexProperty = startingPropertyState; var innerEntry = new ODataResource() { Properties = new ODataProperty[] { new ODataProperty() { Name = "Prop", Value = 1 } } }; @@ -184,7 +184,7 @@ public void ValueShouldBeAppliedRegardlessIfPropertyStartsNullOrNot() [Fact] public void NullValueShouldBeAppliedToSubComplexValueProperty() { - TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + TestMaterializerContext materializerContext = new TestMaterializerContext(new MaterializerCache()); ComplexTypeWithChildComplexType complexInstance = new ComplexTypeWithChildComplexType(); complexInstance.InnerComplexProperty = new ChildComplexType(); @@ -194,7 +194,7 @@ public void NullValueShouldBeAppliedToSubComplexValueProperty() private void ApplyInnerProperty(ODataResource innerResource, ComplexTypeWithChildComplexType parentInstance, TestMaterializerContext materializerContext = null) { - materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerAnnotationsCache()); + materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerCache()); var resource = new ODataResource() { TypeName = "ComplexTypeWithChildComplexType", Properties = new ODataProperty[0] }; var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); @@ -224,7 +224,7 @@ internal EntryValueMaterializationPolicy CreateEntryMaterializationPolicy(TestMa { var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); - materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerCache()) { Model = clientEdmModel, Context = context }; var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context, materializerContext); var lazyPrimitivePropertyConverter = new DSClient.SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter()); var primitiveValueMaterializerPolicy = new PrimitiveValueMaterializationPolicy(materializerContext, lazyPrimitivePropertyConverter); @@ -274,7 +274,7 @@ public void ShouldMaterializeConcreteComplexCollectionDeclaredAsAbstract() new ODataResource(){Properties = new ODataProperty[]{ new ODataProperty(){Name="Points", Value = 0}, new ODataProperty(){Name="Diameter", Value = 18} }}, }); - var testContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var testContext = new TestMaterializerContext(new MaterializerCache()); testContext.ResolveTypeForMaterializationOverrideFunc = (Type type, string name) => { var edmType = testContext.Model.GetOrCreateEdmType(typeof(CollectionValueMaterializationPolicyTests.Circle)); @@ -328,7 +328,7 @@ internal ODataEntriesEntityMaterializer CreateODataEntriesEntityMaterializer( { var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); - materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerCache()) { Model = clientEdmModel, Context = context }; var resourceSet = new ODataResourceSet(); MaterializerFeed.CreateFeed(resourceSet, resources, materializerContext); diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryValueMaterializationPolicyUnitTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryValueMaterializationPolicyUnitTests.cs index fa5ae38c07..d8e7ed2303 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryValueMaterializationPolicyUnitTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/EntryValueMaterializationPolicyUnitTests.cs @@ -41,7 +41,7 @@ public EntryValueMaterializationPolicyUnitTests() this.clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); this.clientEdmModel.GetOrCreateEdmType(typeof(TestCustomer)); this.clientEdmModel.GetOrCreateEdmType(typeof(TestOrder)); - this.materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = this.clientEdmModel }; + this.materializerContext = new TestMaterializerContext(new MaterializerCache()) { Model = this.clientEdmModel }; this.ordersProperty = this.clientEdmModel.GetClientTypeAnnotation(typeof(TestCustomer)).GetProperty("Orders", UndeclaredPropertyBehavior.ThrowException); } diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/FeedAndEntryMaterializerAdapterUnitTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/FeedAndEntryMaterializerAdapterUnitTests.cs index 09a01fa39a..e9805920b9 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/FeedAndEntryMaterializerAdapterUnitTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/FeedAndEntryMaterializerAdapterUnitTests.cs @@ -52,7 +52,7 @@ public void ValidateShortIntegrationFeedReading() var responsePipeline = new DataServiceClientResponsePipelineConfiguration(new DataServiceContext()); var odataReaderWrapper = ODataReaderWrapper.CreateForTest(testODataReader, responsePipeline); - var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var materializerContext = new TestMaterializerContext(new MaterializerCache()); FeedAndEntryMaterializerAdapter reader = new FeedAndEntryMaterializerAdapter(ODataFormat.Json, odataReaderWrapper, clientEdmModel, MergeOption.OverwriteChanges, materializerContext); int readCounter = 0; diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/MaterializerEntryTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/MaterializerEntryTests.cs index 8a455e455b..ab815f798b 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/MaterializerEntryTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/MaterializerEntryTests.cs @@ -64,7 +64,7 @@ private MaterializerEntry CreateMaterializerEntry(ODataFormat format, Action<ODa modifyEntry(entry); } - var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var materializerContext = new TestMaterializerContext(new MaterializerCache()); return MaterializerEntry.CreateEntry(entry, format, true, this.clientModel, materializerContext); } } diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntityMaterializerUnitTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntityMaterializerUnitTests.cs index 23d771e718..de1b7611ab 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntityMaterializerUnitTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntityMaterializerUnitTests.cs @@ -31,7 +31,7 @@ public ODataEntityMaterializerUnitTests() [Fact] public void AfterEntryMaterializedShouldOccur() { - var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var materializerContext = new TestMaterializerContext(new MaterializerCache()); foreach (ODataFormat format in new ODataFormat[] { ODataFormat.Json }) { diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntriesEntityMaterializerUnitTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntriesEntityMaterializerUnitTests.cs index 0b7550541b..69f3aefa88 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntriesEntityMaterializerUnitTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/ODataEntriesEntityMaterializerUnitTests.cs @@ -30,7 +30,7 @@ public void ShortIntegrationTestToValidateEntryShouldBeRead() var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); - var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + var materializerContext = new TestMaterializerContext(new MaterializerCache()) { Model = clientEdmModel, Context = context }; MaterializerEntry.CreateEntry(odataEntry, ODataFormat.Json, true, clientEdmModel, materializerContext); var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context, materializerContext); diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/PrimitiveValueMaterializationPolicyTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/PrimitiveValueMaterializationPolicyTests.cs index 2da7be54a6..1d35ad1840 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/PrimitiveValueMaterializationPolicyTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/PrimitiveValueMaterializationPolicyTests.cs @@ -63,7 +63,7 @@ public void TimeOfDayValueShouldMaterializeCorrectly() internal PrimitiveValueMaterializationPolicy CreatePrimitiveValueMaterializationPolicy() { - return new PrimitiveValueMaterializationPolicy(new TestMaterializerContext(new MaterializerAnnotationsCache()), new SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter())); + return new PrimitiveValueMaterializationPolicy(new TestMaterializerContext(new MaterializerCache()), new SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter())); } public class UnknownPoint diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/TestMaterializerContext.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/TestMaterializerContext.cs index 67bbf93737..246919b7c8 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/TestMaterializerContext.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/Materialization/TestMaterializerContext.cs @@ -17,13 +17,13 @@ namespace AstoriaUnitTests.Tests /// </summary> internal class TestMaterializerContext : IODataMaterializerContext { - public TestMaterializerContext(MaterializerAnnotationsCache annotationsCache) + public TestMaterializerContext(MaterializerCache materializerCache) { this.UndeclaredPropertyBehavior = UndeclaredPropertyBehavior.ThrowException; this.ResponsePipeline = new DataServiceClientResponsePipelineConfiguration(this); this.Model = new ClientEdmModel(ODataProtocolVersion.V4); this.Context = new DataServiceContext(); - this.AnnotationsCache = annotationsCache; + this.MaterializerCache = materializerCache; } public Func<Type, string, ClientTypeAnnotation> ResolveTypeForMaterializationOverrideFunc { get; set; } @@ -52,6 +52,6 @@ public IEdmType ResolveExpectedTypeForReading(Type expectedType) public DataServiceContext Context { get; set; } - public MaterializerAnnotationsCache AnnotationsCache { get; private set; } + public MaterializerCache MaterializerCache { get; private set; } } } diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/T4/ODataT4CamelCaseTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/T4/ODataT4CamelCaseTests.cs index 3a7546d2d9..e24036be19 100644 --- a/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/T4/ODataT4CamelCaseTests.cs +++ b/test/FunctionalTests/Tests/DataServices/UnitTests/Client.TDD.Tests/Tests/T4/ODataT4CamelCaseTests.cs @@ -322,7 +322,7 @@ public void MaterializeEntityShouldWork() var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); - var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + var materializerContext = new TestMaterializerContext(new MaterializerCache()) { Model = clientEdmModel, Context = context }; var materializerEntry = MaterializerEntry.CreateEntry(odataEntry, OData.ODataFormat.Json, true, clientEdmModel, materializerContext); MaterializerNavigationLink.CreateLink(complexP, MaterializerEntry.CreateEntry(complexResource, OData.ODataFormat.Json, true, clientEdmModel, materializerContext), materializerContext); @@ -382,7 +382,7 @@ public void MaterializeComplexTypeShouldWork() } }; - var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var materializerContext = new TestMaterializerContext(new MaterializerCache()); var materializerEntry = MaterializerEntry.CreateEntry(complexValue, OData.ODataFormat.Json, false, new ClientEdmModel(ODataProtocolVersion.V4), materializerContext); this.CreateEntryMaterializationPolicy().Materialize(materializerEntry, typeof(ComplexType), false); var complex = materializerEntry.ResolvedObject as ComplexType; @@ -398,7 +398,7 @@ public void MaterializeEnumTypeShouldWork() { OData.ODataEnumValue enumValue = new OData.ODataEnumValue("blue"); OData.ODataProperty property = new OData.ODataProperty { Name = "enumProperty", Value = enumValue }; - var materializerContext = new TestMaterializerContext(new MaterializerAnnotationsCache()); + var materializerContext = new TestMaterializerContext(new MaterializerCache()); var enumPolicy = new EnumValueMaterializationPolicy(materializerContext); var result = enumPolicy.MaterializeEnumTypeProperty(typeof(Color), property); property.GetMaterializedValue(materializerContext).Should().Be(Color.Blue); @@ -473,7 +473,7 @@ internal EntryValueMaterializationPolicy CreateEntryMaterializationPolicy(TestMa { var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext().ReConfigureForNetworkLoadingTests(); - materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerAnnotationsCache()) { Model = clientEdmModel, Context = context }; + materializerContext = materializerContext ?? new TestMaterializerContext(new MaterializerCache()) { Model = clientEdmModel, Context = context }; var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context, materializerContext); var lazyPrimitivePropertyConverter = new Microsoft.OData.Client.SimpleLazy<PrimitivePropertyConverter>(() => new PrimitivePropertyConverter()); var primitiveValueMaterializerPolicy = new PrimitiveValueMaterializationPolicy(materializerContext, lazyPrimitivePropertyConverter); From 310e03b533913b19ad3fa7dfd879300edd017c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Habinshuti?= <haby_habbes@live.com> Date: Mon, 19 Sep 2022 10:47:21 +0300 Subject: [PATCH 16/16] Update src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs Co-authored-by: Kennedy Kang'ethe <kemunga@microsoft.com> --- .../Materialization/IODataMaterializerContext.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs b/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs index 9f435bce90..a1baef5838 100644 --- a/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs +++ b/src/Microsoft.OData.Client/Materialization/IODataMaterializerContext.cs @@ -54,7 +54,8 @@ internal interface IODataMaterializerContext IEdmType ResolveExpectedTypeForReading(Type clientClrType); /// <summary> - /// Used to store temporary metadata used to converter deserialized + /// Used to store temporary metadata used to convert deserialized + /// OData items into CLR objects. /// </summary> MaterializerCache MaterializerCache { get; }