Exemple #1
0
        /// <summary>
        /// Reads a navigation link.
        /// </summary>
        /// <returns>A navigation link.</returns>
        private ODataNestedResourceInfo ReadNestedResourceInfo()
        {
            Debug.Assert(this.reader.State == ODataReaderState.NestedResourceInfoStart, "this.reader.State == ODataReaderState.NestedResourceInfoStart");

            ODataNestedResourceInfo link = (ODataNestedResourceInfo)this.reader.Item;

            MaterializerEntry entry;
            ODataResourceSet  feed;

            if (this.TryReadFeedOrEntry(false, out feed, out entry))
            {
                if (feed != null)
                {
                    MaterializerNavigationLink.CreateLink(link, feed);
                }
                else
                {
                    Debug.Assert(entry != null, "entry != null");
                    MaterializerNavigationLink.CreateLink(link, entry);
                }

                this.ReadAndExpectState(ODataReaderState.NestedResourceInfoEnd);
            }

            this.ExpectState(ODataReaderState.NestedResourceInfoEnd);

            return(link);
        }
Exemple #2
0
        /// <summary>
        /// Creates the materializer link with a resource set.
        /// </summary>
        /// <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)
        {
            Debug.Assert(link.GetAnnotation <MaterializerNavigationLink>() == null, "there should be no MaterializerNestedResourceInfo annotation on the feed link yet");
            MaterializerNavigationLink materializedNestedResourceInfo = new MaterializerNavigationLink(link, resourceSet);

            link.SetAnnotation <MaterializerNavigationLink>(materializedNestedResourceInfo);
            return(materializedNestedResourceInfo);
        }
Exemple #3
0
        private void ApplyInnerProperty(ODataResource innerResource, ComplexTypeWithChildComplexType parentInstance, TestMaterializerContext context = null)
        {
            context = context ?? new TestMaterializerContext();
            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);

            materializerEntry.ResolvedObject = parentInstance;
            ODataNestedResourceInfo innerComplexP = new ODataNestedResourceInfo()
            {
                Name = "InnerComplexProperty", IsCollection = false
            };

            MaterializerEntry innerMaterializerEntry;

            if (innerResource != null)
            {
                innerMaterializerEntry = MaterializerEntry.CreateEntry(innerResource, ODataFormat.Json, true, clientEdmModel);
            }
            else
            {
                innerMaterializerEntry = MaterializerEntry.CreateEmpty();
            }

            MaterializerNavigationLink.CreateLink(innerComplexP, innerMaterializerEntry);
            materializerEntry.AddNestedResourceInfo(innerComplexP);

            var policy = this.CreateEntryMaterializationPolicy(context);

            policy.EntityTrackingAdapter.TargetInstance = parentInstance;
            policy.Materialize(materializerEntry, typeof(ComplexTypeWithChildComplexType), true);
        }
Exemple #4
0
        /// <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);
                }
            }
        }
Exemple #5
0
        /// <summary>Materializes the specified <paramref name="entry"/>.</summary>
        /// <param name="entry">Entry with object to materialize.</param>
        /// <param name="includeLinks">Whether links that are expanded for navigation property should be materialized.</param>
        /// <remarks>This is a payload-driven materialization process.</remarks>
        private void MaterializeResolvedEntry(MaterializerEntry entry, bool includeLinks)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!");

            ClientTypeAnnotation actualType = entry.ActualType;

            // While materializing entities, we need to make sure the payload that came in the wire is also an entity.
            // Otherwise we need to fail.
            // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa
            if (!actualType.IsStructuredType)
            {
                throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(actualType.ElementTypeName));
            }

            // Note that even if ShouldUpdateFromPayload is false, we will still be creating
            // nested instances (but not their links), so they show up in the data context
            // entries. This keeps this code compatible with V1 behavior.
            this.MaterializeDataValues(actualType, entry.Properties, this.MaterializerContext.UndeclaredPropertyBehavior);

            if (entry.NestedResourceInfos?.Any() == true)
            {
                foreach (ODataNestedResourceInfo link in entry.NestedResourceInfos)
                {
                    var prop = actualType.GetProperty(link.Name, UndeclaredPropertyBehavior.Support);
                    if (prop != null)
                    {
                        ValidatePropertyMatch(prop, link, this.MaterializerContext.Model, true /*performEntityCheck*/);
                    }
                }

                foreach (ODataNestedResourceInfo link in entry.NestedResourceInfos)
                {
                    MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link);

                    if (linkState == null)
                    {
                        continue;
                    }

                    var prop = actualType.GetProperty(link.Name, this.MaterializerContext.UndeclaredPropertyBehavior);

                    if (prop == null)
                    {
                        if (entry.ShouldUpdateFromPayload)
                        {
                            this.MaterializeDynamicProperty(entry, link);
                        }

                        continue;
                    }

                    // includeLinks is for Navigation property, so we should handle complex property when includeLinks equals false;
                    if (!includeLinks && (prop.IsEntityCollection || prop.EntityCollectionItemType != null))
                    {
                        continue;
                    }

                    if (linkState.Feed != null)
                    {
                        this.ApplyFeedToCollection(entry, prop, linkState.Feed, includeLinks);
                    }
                    else if (linkState.Entry != null)
                    {
                        MaterializerEntry linkEntry = linkState.Entry;

                        if (linkEntry.Entry != null)
                        {
                            this.Materialize(linkEntry, prop.PropertyType, includeLinks);
                        }

                        if (entry.ShouldUpdateFromPayload)
                        {
                            prop.SetValue(entry.ResolvedObject, linkEntry.ResolvedObject, link.Name, true /* allowAdd? */);

                            if (!this.MaterializerContext.Context.DisableInstanceAnnotationMaterialization && linkEntry.ShouldUpdateFromPayload)
                            {
                                // Apply instance annotation for navigation property
                                this.InstanceAnnotationMaterializationPolicy.SetInstanceAnnotations(
                                    prop.PropertyName, linkEntry.Entry, entry.ActualType.ElementType, entry.ResolvedObject);

                                // Apply instance annotation for entity of the navigation property
                                this.InstanceAnnotationMaterializationPolicy.SetInstanceAnnotations(linkEntry.Entry, linkEntry.ResolvedObject);
                            }

                            this.EntityTrackingAdapter.MaterializationLog.SetLink(entry, prop.PropertyName, linkEntry.ResolvedObject);
                        }
                    }
                }
            }

            foreach (var e in entry.Properties)
            {
                if (e.Value is ODataStreamReferenceValue)
                {
                    continue;
                }

                var prop = actualType.GetProperty(e.Name, this.MaterializerContext.UndeclaredPropertyBehavior);
                if (prop == null)
                {
                    if (entry.ShouldUpdateFromPayload)
                    {
                        this.MaterializeDynamicProperty(e, entry.ResolvedObject);
                    }

                    continue;
                }

                if (entry.ShouldUpdateFromPayload)
                {
                    ValidatePropertyMatch(prop, e, this.MaterializerContext.Model, true /*performEntityCheck*/);

                    this.ApplyDataValue(actualType, e, entry.ResolvedObject);
                }
            }

            // apply link values if present
            ApplyLinkProperties(actualType, entry);

            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise we didn't do any useful work");

            BaseEntityType entity = entry.ResolvedObject as BaseEntityType;

            if (entity != null)
            {
                entity.Context = this.EntityTrackingAdapter.Context;

                if (!entry.IsTracking)
                {
                    int?           streamDescriptorsCount = entry.EntityDescriptor.StreamDescriptors?.Count;
                    IEdmEntityType entityType             =
                        this.EntityTrackingAdapter.Model.FindDeclaredType(entry.Entry.TypeName) as IEdmEntityType;

                    if (streamDescriptorsCount > 0 || entityType?.HasStream == true)
                    {
                        entity.EntityDescriptor = entry.EntityDescriptor;
                    }
                }
            }

            this.MaterializerContext.ResponsePipeline.FireEndEntryEvents(entry);
        }
Exemple #6
0
        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
            });
        }