/// <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);
        }
 /// <summary>
 /// Creates the materializer link with a feed.
 /// </summary>
 /// <param name="link">The link.</param>
 /// <param name="feed">The feed.</param>
 /// <returns>The materializer link.</returns>
 public static MaterializerNavigationLink CreateLink(ODataNavigationLink link, ODataFeed feed)
 {
     Debug.Assert(link.GetAnnotation<MaterializerNavigationLink>() == null, "there should be no MaterializerNavigationLink annotation on the feed link yet");
     MaterializerNavigationLink materializedNavigationLink = new MaterializerNavigationLink(link, feed);
     link.SetAnnotation<MaterializerNavigationLink>(materializedNavigationLink);
     return materializedNavigationLink;
 }
Beispiel #3
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);
        }
        /// <summary>
        /// Creates the materializer link with a feed.
        /// </summary>
        /// <param name="link">The link.</param>
        /// <param name="feed">The feed.</param>
        /// <returns>The materializer link.</returns>
        public static MaterializerNavigationLink CreateLink(ODataNavigationLink link, ODataFeed feed)
        {
            Debug.Assert(link.GetAnnotation <MaterializerNavigationLink>() == null, "there should be no MaterializerNavigationLink annotation on the feed link yet");
            MaterializerNavigationLink materializedNavigationLink = new MaterializerNavigationLink(link, feed);

            link.SetAnnotation <MaterializerNavigationLink>(materializedNavigationLink);
            return(materializedNavigationLink);
        }
Beispiel #5
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);
                }
            }
        }
Beispiel #6
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;
            }

            this.MaterializerContext.ResponsePipeline.FireEndEntryEvents(entry);
        }
Beispiel #7
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 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.
            if (!actualType.IsEntityType)
            {
                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.IgnoreMissingProperties);

            if (entry.NavigationLinks != null)
            {
                foreach (ODataNavigationLink link in entry.NavigationLinks)
                {
                    var prop = actualType.GetProperty(link.Name, true);
                    if (prop != null)
                    {
                        ValidatePropertyMatch(prop, link, this.MaterializerContext.Model, true /*performEntityCheck*/);
                    }
                }

                if (includeLinks)
                {
                    foreach (ODataNavigationLink link in entry.NavigationLinks)
                    {
                        MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link);

                        // Ignore...
                        if (linkState == null)
                        {
                            continue;
                        }

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

                        if (prop == 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)
                            {
                                Debug.Assert(includeLinks, "includeLinks -- otherwise we shouldn't be materializing this entry");
                                this.Materialize(linkEntry, prop.PropertyType, includeLinks);
                            }

                            if (entry.ShouldUpdateFromPayload)
                            {
                                prop.SetValue(entry.ResolvedObject, linkEntry.ResolvedObject, link.Name, true /* allowAdd? */);
                                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.IgnoreMissingProperties);
                if (prop == null)
                {
                    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;
            }

            this.MaterializerContext.ResponsePipeline.FireEndEntryEvents(entry);
        }