/// <summary> /// Applies the values of a nested <paramref name="feed"/> to the collection /// <paramref name="property"/> of the specified <paramref name="entry"/>. /// </summary> /// <param name="entry">Entry with collection to be modified.</param> /// <param name="property">Collection property on the entry.</param> /// <param name="feed">Values to apply onto the collection.</param> /// <param name="includeLinks">Whether links that are expanded should be materialized.</param> private void ApplyFeedToCollection( MaterializerEntry entry, ClientPropertyAnnotation property, ODataResourceSet feed, bool includeLinks) { Debug.Assert(entry.Entry != null, "entry != null"); Debug.Assert(property != null, "property != null"); Debug.Assert(feed != null, "feed != null"); ClientEdmModel edmModel = this.MaterializerContext.Model; ClientTypeAnnotation collectionType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(property.ResourceSetItemType)); IEnumerable <ODataResource> entries = MaterializerFeed.GetFeed(feed).Entries; foreach (ODataResource feedEntry in entries) { this.Materialize(MaterializerEntry.GetEntry(feedEntry), collectionType.ElementType, includeLinks); } ProjectionPlan continuationPlan = includeLinks ? ODataEntityMaterializer.CreatePlanForDirectMaterialization(property.ResourceSetItemType) : ODataEntityMaterializer.CreatePlanForShallowMaterialization(property.ResourceSetItemType); this.ApplyItemsToCollection( entry, property, entries.Select(e => MaterializerEntry.GetEntry(e).ResolvedObject), feed.NextPageLink, continuationPlan, false); }
internal ODataEntriesEntityMaterializer CreateODataEntriesEntityMaterializer( List <ODataResource> resources, Type resourceType, TestMaterializerContext materializerContext = null) { var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); var resourceSet = new ODataResourceSet(); MaterializerFeed.CreateFeed(resourceSet, resources); resources.ForEach(r => { if (r == null) { MaterializerEntry.CreateEmpty(); } else { MaterializerEntry.CreateEntry(r, ODataFormat.Json, true, clientEdmModel); } }); materializerContext = materializerContext ?? new TestMaterializerContext() { Model = clientEdmModel, Context = context }; var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context); 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)); }
/// <summary> /// The count tag's value, if requested /// </summary> /// <param name="readIfNoFeed">Should read pull if no feed exists.</param> /// <returns>The count value returned from the server</returns> 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(); } if (this.currentFeed != null && this.currentFeed.Count.HasValue) { return(this.currentFeed.Count.Value); } throw new InvalidOperationException(DSClient.Strings.MaterializeFromAtom_CountNotPresent); }
/// <summary> /// Reads the remainder of a feed. /// </summary> /// <param name="lazy">if set to <c>true</c> [lazy].</param> /// <returns>A feed.</returns> private ODataResourceSet ReadFeedCore(bool lazy) { this.ExpectState(ODataReaderState.ResourceSetStart); ODataResourceSet result = (ODataResourceSet)this.reader.Item; IEnumerable <ODataResource> lazyEntries = this.LazyReadEntries(); if (lazy) { MaterializerFeed.CreateFeed(result, lazyEntries); } else { MaterializerFeed.CreateFeed(result, new List <ODataResource>(lazyEntries)); } return(result); }
/// <summary>Materializes the specified <paramref name="entry"/> as dynamic property.</summary> /// <param name="entry">Entry with object to materialize.</param> /// <param name="link">Nested resource link as parsed.</param> private void MaterializeDynamicProperty(MaterializerEntry entry, ODataNestedResourceInfo link) { Debug.Assert(entry != null, "entry != null"); Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!"); Debug.Assert(link != null, "link != null"); ClientEdmModel model = this.MaterializerContext.Model; IDictionary <string, object> containerProperty; // Stop if owning type is not an open type // Or container property is not found // Or key with matching name already exists in the dictionary if (!ClientTypeUtil.IsInstanceOfOpenType(entry.ResolvedObject, model) || !ClientTypeUtil.TryGetContainerProperty(entry.ResolvedObject, out containerProperty) || containerProperty.ContainsKey(link.Name)) { return; } MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link); if (linkState == null || (linkState.Entry == null && linkState.Feed == null)) { return; } // NOTE: ODL (and OData WebApi) support navigational property on complex types // That support has not yet been implemented in OData client // An entity or entity collection as a dynamic property currently doesn't work as expected // due to the absence of a navigational property definition in the metadata // to express the relationship between that entity and the parent entity - unless they're the same type! // Only materialize a nested resource if its a complex or complex collection if (linkState.Feed != null) { string collectionTypeName = linkState.Feed.TypeName; // TypeName represents a collection e.g. Collection(NS.Type) string collectionItemTypeName = CommonUtil.GetCollectionItemTypeName(collectionTypeName, false); // Highly unlikely, but the method return null if the typeName argument does not meet certain expectations if (collectionItemTypeName == null) { return; } Type collectionItemType = ResolveClientTypeForDynamicProperty(collectionItemTypeName, entry.ResolvedObject); if (collectionItemType != null && ClientTypeUtil.TypeIsComplex(collectionItemType, model)) { 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; foreach (ODataResource feedEntry in feedEntries) { MaterializerEntry linkEntry = MaterializerEntry.GetEntry(feedEntry); this.Materialize(linkEntry, collectionItemType, false /*includeLinks*/); collection.Add(linkEntry.ResolvedObject); } containerProperty.Add(link.Name, collection); } } else { MaterializerEntry linkEntry = linkState.Entry; Type itemType = ResolveClientTypeForDynamicProperty(linkEntry.Entry.TypeName, entry.ResolvedObject); if (itemType != null && ClientTypeUtil.TypeIsComplex(itemType, model)) { this.Materialize(linkEntry, itemType, false /*includeLinks*/); containerProperty.Add(link.Name, linkEntry.ResolvedObject); } } }
public void MaterializeEntityShouldWork() { var odataEntry = new OData.ODataResource() { Id = new Uri("http://www.odata.org/service.svc/entitySet(1)") }; odataEntry.Properties = new OData.ODataProperty[] { new OData.ODataProperty { Name = "keyProp", Value = 1 }, new OData.ODataProperty { Name = "colorProp", Value = new OData.ODataEnumValue("blue") }, new OData.ODataProperty { Name = "primitiveCollection", Value = new OData.ODataCollectionValue { TypeName = "Edm.Int32", Items = new List <object> { 1, 2, 3 } } }, new OData.ODataProperty { Name = "enumCollection", Value = new OData.ODataCollectionValue { TypeName = "color", Items = new List <OData.ODataEnumValue> { new OData.ODataEnumValue("white"), new OData.ODataEnumValue("blue") } } } }; var complexP = new OData.ODataNestedResourceInfo() { Name = "complexProp", IsCollection = false }; var complexResource = new OData.ODataResource { Properties = new OData.ODataProperty[] { new OData.ODataProperty { Name = "age", Value = 11 }, new OData.ODataProperty { Name = "name", Value = "June" } } }; var complexColP = new OData.ODataNestedResourceInfo { Name = "complexCollection", IsCollection = true }; var complexColResourceSet = new OData.ODataResourceSet(); var items = new List <OData.ODataResource> { new OData.ODataResource { Properties = new OData.ODataProperty[] { new OData.ODataProperty { Name = "name", Value = "Aug" }, new OData.ODataProperty { Name = "age", Value = 8 }, new OData.ODataProperty { Name = "color", Value = new OData.ODataEnumValue("white") } } }, new OData.ODataResource { Properties = new OData.ODataProperty[] { new OData.ODataProperty { Name = "name", Value = "Sep" }, new OData.ODataProperty { Name = "age", Value = 9 }, new OData.ODataProperty { Name = "color", Value = new OData.ODataEnumValue("blue") } } } }; var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); var materializerEntry = MaterializerEntry.CreateEntry(odataEntry, OData.ODataFormat.Json, true, clientEdmModel); MaterializerNavigationLink.CreateLink(complexP, MaterializerEntry.CreateEntry(complexResource, OData.ODataFormat.Json, true, clientEdmModel)); MaterializerFeed.CreateFeed(complexColResourceSet, items); MaterializerNavigationLink.CreateLink(complexColP, complexColResourceSet); var materializerContext = new TestMaterializerContext() { Model = clientEdmModel, Context = context }; var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context); 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); var customersRead = new List <EntityType>(); while (entriesMaterializer.Read()) { customersRead.Add(entriesMaterializer.CurrentValue as EntityType); } customersRead.Should().HaveCount(1); customersRead[0].KeyProp.Should().Be(1); customersRead[0].ComplexProp.Should().Equals(new ComplexType { Age = 11, Name = "June" }); customersRead[0].ColorProp.Should().Equals(Color.Blue); customersRead[0].PrimitiveCollection.Should().Equals(new List <int> { 1, 2, 3 }); ComplexType complex1 = new ComplexType { Name = "Aug", Age = 8, Color = Color.White }; ComplexType complex2 = new ComplexType { Name = "Sep", Age = 9, Color = Color.Blue }; customersRead[0].ComplexCollection.Should().Equals(new List <ComplexType> { complex1, complex2 }); customersRead[0].EnumCollection.Should().Equals(new List <Color> { Color.White, Color.Blue }); }
/// <summary> /// Read a feed or entry, with the expected type. /// </summary> /// <returns>true if a value was read, otherwise false</returns> public bool Read() { if (this.feedEntries != null) { // ISSUE: this might throw - refactor? if (this.feedEntries.MoveNext()) { this.currentEntry = this.feedEntries.Current; return(true); } else { this.feedEntries = null; this.currentEntry = null; } } switch (this.reader.State) { case ODataReaderState.Completed: this.currentEntry = null; return(false); case ODataReaderState.Start: { ODataResourceSet feed; MaterializerEntry entryAndState; if (this.TryReadFeedOrEntry(true, out feed, out entryAndState)) { this.currentEntry = entryAndState != null ? entryAndState.Entry : null; this.currentFeed = feed; if (this.currentFeed != null) { this.feedEntries = MaterializerFeed.GetFeed(this.currentFeed).Entries.GetEnumerator(); // Try to read the first entry. if (!this.feedEntries.MoveNext()) { this.feedEntries = null; this.currentEntry = null; return(false); } else { this.currentEntry = this.feedEntries.Current; } } return(true); } else { throw new NotImplementedException(); } } case ODataReaderState.ResourceSetEnd: case ODataReaderState.ResourceEnd: if (this.TryRead() || this.reader.State != ODataReaderState.Completed) { throw DSClient.Error.InternalError(InternalError.UnexpectedReadState); } this.currentEntry = null; return(false); default: throw DSClient.Error.InternalError(InternalError.UnexpectedReadState); } }