Beispiel #1
0
        /// <summary>
        /// Returns a client type of the property on the specified resource type.
        /// </summary>
        /// <param name="clientType">The client type to look for the property on.</param>
        /// <param name="propertyName">The name of the property to look for.</param>
        /// <returns>The type of the property specified. Note that for collection properties this returns the type of the item of the collection property.</returns>
        private static ClientTypeAnnotation GetPropertyType(ClientTypeAnnotation clientType, string propertyName)
        {
            Debug.Assert(propertyName != null, "propertyName != null");

            ClientPropertyAnnotation clientProperty = clientType.GetProperty(propertyName, true);

            if (clientProperty == null)
            {
                throw c.Error.InvalidOperation(c.Strings.EpmSourceTree_InaccessiblePropertyOnType(propertyName, clientType.ElementTypeName));
            }

            if (clientProperty.IsStreamLinkProperty)
            {
                throw c.Error.InvalidOperation(c.Strings.EpmSourceTree_NamedStreamCannotBeMapped(propertyName, clientType.ElementTypeName));
            }

            if (clientProperty.IsSpatialType)
            {
                throw c.Error.InvalidOperation(c.Strings.EpmSourceTree_SpatialTypeCannotBeMapped(propertyName, clientType.ElementTypeName));
            }

            if (clientProperty.IsPrimitiveOrComplexCollection)
            {
                throw c.Error.InvalidOperation(c.Strings.EpmSourceTree_CollectionPropertyCannotBeMapped(propertyName, clientType.ElementTypeName));
            }

            ClientEdmModel model    = clientProperty.Model;
            IEdmType       edmType1 = model.GetOrCreateEdmType(clientProperty.PropertyType);
            var            edmType  = edmType1;

            return(model.GetClientTypeAnnotation(edmType));
        }
Beispiel #2
0
        internal void WriteNavigationLink(EntityDescriptor entityDescriptor, IEnumerable <LinkDescriptor> relatedLinks, ODataWriter odataWriter)
        {
            ClientTypeAnnotation clientTypeAnnotation = null;

            foreach (LinkDescriptor descriptor in relatedLinks)
            {
                descriptor.ContentGeneratedForSave = true;
                if (clientTypeAnnotation == null)
                {
                    ClientEdmModel model = ClientEdmModel.GetModel(this.requestInfo.MaxProtocolVersion);
                    clientTypeAnnotation = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
                }
                ODataNavigationLink navigationLink = new ODataNavigationLink {
                    Url          = this.requestInfo.EntityTracker.GetEntityDescriptor(descriptor.Target).GetLatestEditLink(),
                    IsCollection = new bool?(clientTypeAnnotation.GetProperty(descriptor.SourceProperty, false).IsEntityCollection),
                    Name         = descriptor.SourceProperty
                };
                odataWriter.WriteStart(navigationLink);
                ODataEntityReferenceLink entityReferenceLink = new ODataEntityReferenceLink {
                    Url = navigationLink.Url
                };
                odataWriter.WriteEntityReferenceLink(entityReferenceLink);
                odataWriter.WriteEnd();
            }
        }
Beispiel #3
0
        private static ClientTypeAnnotation GetPropertyType(ClientTypeAnnotation clientType, string propertyName)
        {
            ClientPropertyAnnotation property = clientType.GetProperty(propertyName, true);

            if (property == null)
            {
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.EpmSourceTree_InaccessiblePropertyOnType(propertyName, clientType.ElementTypeName));
            }
            if (property.IsStreamLinkProperty)
            {
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.EpmSourceTree_NamedStreamCannotBeMapped(propertyName, clientType.ElementTypeName));
            }
            if (property.IsSpatialType)
            {
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.EpmSourceTree_SpatialTypeCannotBeMapped(propertyName, clientType.ElementTypeName));
            }
            if (property.IsPrimitiveOrComplexCollection)
            {
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.EpmSourceTree_CollectionPropertyCannotBeMapped(propertyName, clientType.ElementTypeName));
            }
            ClientEdmModel model           = ClientEdmModel.GetModel(clientType.MaxProtocolVersion);
            IEdmType       orCreateEdmType = model.GetOrCreateEdmType(property.PropertyType);

            return(model.GetClientTypeAnnotation(orCreateEdmType));
        }
        /// <summary>
        /// Materializes the primitive data values in the given list of <paramref name="values"/>.
        /// </summary>
        /// <param name="actualType">Actual type for properties being materialized.</param>
        /// <param name="values">List of values to materialize.</param>
        /// <param name="undeclaredPropertyBehavior">
        /// Whether properties missing from the client types should be supported or throw exception.
        /// </param>
        /// <remarks>
        /// Values are materialized in-place with each <see cref="ODataProperty"/>
        /// instance.
        /// </remarks>
        internal void MaterializeDataValues(ClientTypeAnnotation actualType, IEnumerable <ODataProperty> values, UndeclaredPropertyBehavior undeclaredPropertyBehavior)
        {
            Debug.Assert(actualType != null, "actualType != null");
            Debug.Assert(values != null, "values != null");

            foreach (var odataProperty in values)
            {
                if (odataProperty.Value is ODataStreamReferenceValue)
                {
                    continue;
                }

                string propertyName = odataProperty.Name;

                var property = actualType.GetProperty(propertyName, undeclaredPropertyBehavior); // may throw
                if (property == null)
                {
                    continue;
                }

                // we should throw if the property type on the client does not match with the property type in the server
                // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa
                if (ClientTypeUtil.TypeOrElementTypeIsEntity(property.PropertyType))
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidEntityType(property.EntityCollectionItemType ?? property.PropertyType));
                }

                if (property.IsKnownType)
                {
                    // Note: MaterializeDataValue materializes only properties of primitive types. Materialization specific
                    // to complex types and collections is done later.
                    this.MaterializePrimitiveDataValue(property.NullablePropertyType, odataProperty);
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Set instance annotation for a property
        /// </summary>
        /// <param name="propertyName">Property name</param>
        /// <param name="instanceAnnotations">Intance annotations to be set</param>
        /// <param name="type">The type of the containing object</param>
        /// <param name="declaringInstance">The containing object instance</param>
        private void SetInstanceAnnotations(string propertyName, IDictionary <string, object> instanceAnnotations, Type type, object declaringInstance)
        {
            if (declaringInstance != null)
            {
                UndeclaredPropertyBehavior undeclaredPropertyBehavior = this.MaterializerContext.Context.UndeclaredPropertyBehavior;

                // Get the client property info
                ClientEdmModel             edmModel                 = this.MaterializerContext.Model;
                ClientTypeAnnotation       clientTypeAnnotation     = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(type));
                ClientPropertyAnnotation   clientPropertyAnnotation = clientTypeAnnotation.GetProperty(propertyName, undeclaredPropertyBehavior);
                Tuple <object, MemberInfo> annotationKeyForProperty = new Tuple <object, MemberInfo>(declaringInstance, clientPropertyAnnotation.PropertyInfo);
                SetInstanceAnnotations(annotationKeyForProperty, instanceAnnotations);
            }
        }
Beispiel #6
0
        /// <summary>
        /// Creates the ResponseInfo object.
        /// </summary>
        /// <returns>ResponseInfo object.</returns>
        protected override ResponseInfo CreateResponseInfo()
        {
            DataServiceContext context = (DataServiceContext)this.Source;

            ClientEdmModel model = context.Model;

            ClientTypeAnnotation type = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(this.entity.GetType()));

            Debug.Assert(type.IsEntityType, "Must be entity type to be contained.");

            return(this.RequestInfo.GetDeserializationInfoForLoadProperty(
                       null,
                       context.GetEntityDescriptor(this.entity),
                       type.GetProperty(this.propertyName, false)));
        }
Beispiel #7
0
        /// <summary>
        /// loading a property from a response
        /// </summary>
        /// <returns>QueryOperationResponse instance containing information about the response.</returns>
        internal QueryOperationResponse LoadProperty()
        {
            MaterializeAtom results = null;

            DataServiceContext context = (DataServiceContext)this.Source;

            ClientEdmModel       model = context.Model;
            ClientTypeAnnotation type  = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(this.entity.GetType()));

            Debug.Assert(type.IsEntityType, "must be entity type to be contained");

            EntityDescriptor box = context.GetEntityDescriptor(this.entity);

            if (EntityStates.Added == box.State)
            {
                throw Error.InvalidOperation(Strings.Context_NoLoadWithInsertEnd);
            }

            ClientPropertyAnnotation property = type.GetProperty(this.propertyName, false);
            Type elementType = property.EntityCollectionItemType ?? property.NullablePropertyType;

            try
            {
                if (type.MediaDataMember == property)
                {
                    results = this.ReadPropertyFromRawData(property);
                }
                else
                {
                    results = this.ReadPropertyFromAtom(property);
                }

                return(this.GetResponseWithType(results, elementType));
            }
            catch (InvalidOperationException ex)
            {
                QueryOperationResponse response = this.GetResponseWithType(results, elementType);
                if (response != null)
                {
                    response.Error = ex;
                    throw new DataServiceQueryException(Strings.DataServiceException_GeneralError, ex, response);
                }

                throw;
            }
        }
Beispiel #8
0
        internal static IEnumerable ProjectionSelect(ODataEntityMaterializer materializer, MaterializerEntry entry, Type expectedType, Type resultType, ProjectionPath path, Func <object, object, Type, object> selector)
        {
            ClientEdmModel             model = ClientEdmModel.GetModel(materializer.ResponseInfo.MaxProtocolVersion);
            ClientTypeAnnotation       clientTypeAnnotation = entry.ActualType ?? model.GetClientTypeAnnotation(model.GetOrCreateEdmType(expectedType));
            IEnumerable                enumerable           = (IEnumerable)Util.ActivatorCreateInstance(typeof(List <>).MakeGenericType(new Type[] { resultType }), new object[0]);
            MaterializerNavigationLink link     = null;
            ClientPropertyAnnotation   property = null;

            for (int i = 0; i < path.Count; i++)
            {
                ProjectionPathSegment segment = path[i];
                if (segment.SourceTypeAs != null)
                {
                    clientTypeAnnotation = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(segment.SourceTypeAs));
                }
                if (segment.Member != null)
                {
                    string member = segment.Member;
                    property = clientTypeAnnotation.GetProperty(member, false);
                    link     = GetPropertyOrThrow(entry.NavigationLinks, member, entry.Id);
                    if (link.Entry != null)
                    {
                        entry = link.Entry;
                        clientTypeAnnotation = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(property.PropertyType));
                    }
                }
            }
            ValidatePropertyMatch(property, link.Link);
            MaterializerFeed        feed = MaterializerFeed.GetFeed(link.Feed);
            Action <object, object> addToCollectionDelegate = ODataMaterializer.GetAddToCollectionDelegate(enumerable.GetType());

            foreach (ODataEntry entry2 in feed.Entries)
            {
                object obj2 = selector(materializer, entry2, property.EntityCollectionItemType);
                addToCollectionDelegate(enumerable, obj2);
            }
            ProjectionPlan plan = new ProjectionPlan {
                LastSegmentType = property.EntityCollectionItemType,
                Plan            = selector,
                ProjectedType   = resultType
            };

            materializer.FoundNextLinkForCollection(enumerable, feed.NextPageLink, plan);
            return(enumerable);
        }
        internal QueryOperationResponse LoadProperty()
        {
            MaterializeAtom        results = null;
            QueryOperationResponse responseWithType;
            DataServiceContext     source = (DataServiceContext)base.Source;
            ClientEdmModel         model  = ClientEdmModel.GetModel(source.MaxProtocolVersion);
            ClientTypeAnnotation   clientTypeAnnotation = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(this.entity.GetType()));
            EntityDescriptor       entityDescriptor     = source.GetEntityDescriptor(this.entity);

            if (EntityStates.Added == entityDescriptor.State)
            {
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Context_NoLoadWithInsertEnd);
            }
            ClientPropertyAnnotation property = clientTypeAnnotation.GetProperty(this.propertyName, false);
            Type elementType = property.EntityCollectionItemType ?? property.NullablePropertyType;

            try
            {
                if (clientTypeAnnotation.MediaDataMember == property)
                {
                    results = this.ReadPropertyFromRawData(property);
                }
                else
                {
                    results = this.ReadPropertyFromAtom(entityDescriptor, property);
                }
                responseWithType = base.GetResponseWithType(results, elementType);
            }
            catch (InvalidOperationException exception)
            {
                QueryOperationResponse response = base.GetResponseWithType(results, elementType);
                if (response != null)
                {
                    response.Error = exception;
                    throw new DataServiceQueryException(System.Data.Services.Client.Strings.DataServiceException_GeneralError, exception, response);
                }
                throw;
            }
            return(responseWithType);
        }
Beispiel #10
0
 protected static void MaterializeDataValues(ClientTypeAnnotation actualType, IEnumerable <ODataProperty> values, bool ignoreMissingProperties)
 {
     foreach (ODataProperty property in values)
     {
         if (!(property.Value is ODataStreamReferenceValue))
         {
             string name = property.Name;
             ClientPropertyAnnotation annotation = actualType.GetProperty(name, ignoreMissingProperties);
             if (annotation != null)
             {
                 if (ClientTypeUtil.TypeOrElementTypeIsEntity(annotation.PropertyType))
                 {
                     throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.AtomMaterializer_InvalidEntityType(annotation.EntityCollectionItemType ?? annotation.PropertyType));
                 }
                 if (annotation.IsKnownType)
                 {
                     MaterializePrimitiveDataValue(annotation.NullablePropertyType, property);
                 }
             }
         }
     }
 }
        /// <summary>Applies a data value to the specified <paramref name="instance"/>.</summary>
        /// <param name="type">Type to which a property value will be applied.</param>
        /// <param name="property">Property with value to apply.</param>
        /// <param name="instance">Instance on which value will be applied.</param>
        internal void ApplyDataValue(ClientTypeAnnotation type, ODataProperty property, object instance)
        {
            Debug.Assert(type != null, "type != null");
            Debug.Assert(property != null, "property != null");
            Debug.Assert(instance != null, "instance != null");

            var prop = type.GetProperty(property.Name, this.MaterializerContext.UndeclaredPropertyBehavior);

            if (prop == null)
            {
                return;
            }

            // Is it a collection? (note: property.Properties will be null if the Collection is empty (contains no elements))
            Type enumTypeTmp = null;

            if (prop.IsPrimitiveOrEnumOrComplexCollection)
            {
                // Collections must not be null
                if (property.Value == null)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.Collection_NullCollectionNotSupported(property.Name));
                }

                // This happens if the payload contain just primitive value for a Collection property
                if (property.Value is string)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MixedTextWithComment);
                }

                // ODataLib already parsed the data and materialized all the primitive types. There is nothing more to materialize
                // anymore. Only complex type instance and collection instances need to be materialized, but those will be
                // materialized later on.
                // We need to materialize items before we change collectionInstance since this may throw. If we tried materializing
                // after the Collection is wiped or created we would leave the object in half constructed state.
                object collectionInstance = prop.GetValue(instance);
                if (collectionInstance == null)
                {
                    collectionInstance = this.CollectionValueMaterializationPolicy.CreateCollectionPropertyInstance(property, prop.PropertyType);

                    // allowAdd is false - we need to assign instance as the new property value
                    prop.SetValue(instance, collectionInstance, property.Name, false /* allowAdd? */);
                }
                else
                {
                    // Clear existing Collection
                    prop.ClearBackingICollectionInstance(collectionInstance);
                }

                bool isElementNullable = prop.EdmProperty.Type.AsCollection().ElementType().IsNullable;
                this.CollectionValueMaterializationPolicy.ApplyCollectionDataValues(
                    property,
                    collectionInstance,
                    prop.PrimitiveOrComplexCollectionItemType,
                    prop.AddValueToBackingICollectionInstance,
                    isElementNullable);
            }
            else if ((enumTypeTmp = Nullable.GetUnderlyingType(prop.NullablePropertyType) ?? prop.NullablePropertyType) != null &&
                     enumTypeTmp.IsEnum())
            {
                ODataEnumValue enumValue = property.Value as ODataEnumValue;
                object         tmpValue  = EnumValueMaterializationPolicy.MaterializeODataEnumValue(enumTypeTmp, enumValue);

                // TODO: 1. use EnumValueMaterializationPolicy 2. handle nullable enum property
                prop.SetValue(instance, tmpValue, property.Name, false /* allowAdd? */);
            }
            else
            {
                this.MaterializePrimitiveDataValue(prop.NullablePropertyType, property);
                prop.SetValue(instance, property.GetMaterializedValue(), property.Name, true /* allowAdd? */);
            }

            if (!this.MaterializerContext.Context.DisableInstanceAnnotationMaterialization)
            {
                // Apply instance annotation for Property
                this.InstanceAnnotationMaterializationPolicy.SetInstanceAnnotations(property, type.ElementType, instance);
            }
        }
Beispiel #12
0
        protected static void ApplyDataValue(ClientTypeAnnotation type, ODataProperty property, bool ignoreMissingProperties, System.Data.Services.Client.ResponseInfo responseInfo, object instance)
        {
            ClientPropertyAnnotation annotation = type.GetProperty(property.Name, ignoreMissingProperties);

            if (annotation != null)
            {
                if (annotation.IsPrimitiveOrComplexCollection)
                {
                    if (property.Value == null)
                    {
                        throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Collection_NullCollectionNotSupported(property.Name));
                    }
                    if (property.Value is string)
                    {
                        throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Deserialize_MixedTextWithComment);
                    }
                    if (property.Value is ODataComplexValue)
                    {
                        throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.AtomMaterializer_InvalidCollectionItem(property.Name));
                    }
                    object obj2 = annotation.GetValue(instance);
                    if (obj2 == null)
                    {
                        obj2 = CreateCollectionInstance(property, annotation.PropertyType, responseInfo);
                        annotation.SetValue(instance, obj2, property.Name, false);
                    }
                    else
                    {
                        annotation.ClearBackingICollectionInstance(obj2);
                    }
                    ApplyCollectionDataValues(property, ignoreMissingProperties, responseInfo, obj2, annotation.PrimitiveOrComplexCollectionItemType, new Action <object, object>(annotation.AddValueToBackingICollectionInstance));
                }
                else
                {
                    object            obj3   = property.Value;
                    ODataComplexValue value2 = obj3 as ODataComplexValue;
                    if ((obj3 != null) && (value2 != null))
                    {
                        if (!annotation.EdmProperty.Type.IsComplex())
                        {
                            throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Deserialize_ExpectingSimpleValue);
                        }
                        bool                 flag  = false;
                        ClientEdmModel       model = ClientEdmModel.GetModel(responseInfo.MaxProtocolVersion);
                        ClientTypeAnnotation clientTypeAnnotation = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(annotation.PropertyType));
                        object               obj4 = annotation.GetValue(instance);
                        if (obj4 == null)
                        {
                            obj4 = clientTypeAnnotation.CreateInstance();
                            flag = true;
                        }
                        MaterializeDataValues(clientTypeAnnotation, value2.Properties, ignoreMissingProperties);
                        ApplyDataValues(clientTypeAnnotation, value2.Properties, ignoreMissingProperties, responseInfo, obj4);
                        if (flag)
                        {
                            annotation.SetValue(instance, obj4, property.Name, true);
                        }
                    }
                    else
                    {
                        MaterializePrimitiveDataValue(annotation.NullablePropertyType, property);
                        annotation.SetValue(instance, property.GetMaterializedValue(), property.Name, true);
                    }
                }
            }
        }
Beispiel #13
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);
        }
Beispiel #14
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);
        }
Beispiel #15
0
        /// <summary>
        /// Writes a navigation link.
        /// </summary>
        /// <param name="entityDescriptor">The entity</param>
        /// <param name="relatedLinks">The links related to the entity</param>
        /// <param name="odataWriter">The ODataWriter used to write the navigation link.</param>
        internal void WriteNestedResourceInfo(EntityDescriptor entityDescriptor, IEnumerable <LinkDescriptor> relatedLinks, ODataWriterWrapper odataWriter)
        {
            // TODO: create instance of odatawriter.
            // TODO: send clientType once, so that we dont need entity descriptor
            Debug.Assert(EntityStates.Added == entityDescriptor.State, "entity not added state");

            Dictionary <string, List <LinkDescriptor> > groupRelatedLinks = new Dictionary <string, List <LinkDescriptor> >(EqualityComparer <string> .Default);

            foreach (LinkDescriptor end in relatedLinks)
            {
                List <LinkDescriptor> linkDescriptorsList = null;
                if (!groupRelatedLinks.TryGetValue(end.SourceProperty, out linkDescriptorsList))
                {
                    linkDescriptorsList = new List <LinkDescriptor>();
                    groupRelatedLinks.Add(end.SourceProperty, linkDescriptorsList);
                }

                linkDescriptorsList.Add(end);
            }

            ClientTypeAnnotation clientType = null;

            foreach (var grlinks in groupRelatedLinks)
            {
                if (null == clientType)
                {
                    ClientEdmModel model = this.requestInfo.Model;
                    clientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
                }

                bool isCollection = clientType.GetProperty(grlinks.Key, UndeclaredPropertyBehavior.ThrowException).IsEntityCollection;
                bool started      = false;

                foreach (LinkDescriptor end in grlinks.Value)
                {
                    Debug.Assert(!end.ContentGeneratedForSave, "already saved link");
                    end.ContentGeneratedForSave = true;
                    Debug.Assert(null != end.Target, "null is DELETE");

                    ODataNestedResourceInfo navigationLink = new ODataNestedResourceInfo();
                    navigationLink.Url = this.requestInfo.EntityTracker.GetEntityDescriptor(end.Target).GetLatestEditLink();
                    Debug.Assert(Uri.IsWellFormedUriString(UriUtil.UriToString(navigationLink.Url), UriKind.Absolute), "Uri.IsWellFormedUriString(targetEditLink, UriKind.Absolute)");

                    navigationLink.IsCollection = isCollection;
                    navigationLink.Name         = grlinks.Key;

                    if (!started)
                    {
                        odataWriter.WriteNestedResourceInfoStart(navigationLink);
                        started = true;
                    }

                    odataWriter.WriteNestedResourceInfoStart(navigationLink, end.Source, end.Target);
                    odataWriter.WriteEntityReferenceLink(new ODataEntityReferenceLink()
                    {
                        Url = navigationLink.Url
                    }, end.Source, end.Target);
                    odataWriter.WriteNestedResourceInfoEnd(navigationLink, end.Source, end.Target);
                }

                odataWriter.WriteNestedResourceInfoEnd();
            }
        }
Beispiel #16
0
        /// <summary>Applies a data value to the specified <paramref name="instance"/>.</summary>
        /// <param name="type">Type to which a property value will be applied.</param>
        /// <param name="property">Property with value to apply.</param>
        /// <param name="instance">Instance on which value will be applied.</param>
        internal void ApplyDataValue(ClientTypeAnnotation type, ODataProperty property, object instance)
        {
            Debug.Assert(type != null, "type != null");
            Debug.Assert(property != null, "property != null");
            Debug.Assert(instance != null, "instance != null");

            var prop = type.GetProperty(property.Name, this.MaterializerContext.IgnoreMissingProperties);

            if (prop == null)
            {
                return;
            }

            // Is it a collection? (note: property.Properties will be null if the Collection is empty (contains no elements))
            Type enumTypeTmp = null;

            if (prop.IsPrimitiveOrEnumOrComplexCollection)
            {
                // Collections must not be null
                if (property.Value == null)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.Collection_NullCollectionNotSupported(property.Name));
                }

                // This happens if the payload contain just primitive value for a Collection property
                if (property.Value is string)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MixedTextWithComment);
                }

                if (property.Value is ODataComplexValue)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidCollectionItem(property.Name));
                }

                // ODataLib already parsed the data and materialized all the primitive types. There is nothing more to materialize
                // anymore. Only complex type instance and collection instances need to be materialized, but those will be
                // materialized later on.
                // We need to materialize items before we change collectionInstance since this may throw. If we tried materializing
                // after the Collection is wiped or created we would leave the object in half constructed state.
                object collectionInstance = prop.GetValue(instance);
                if (collectionInstance == null)
                {
                    collectionInstance = this.CollectionValueMaterializationPolicy.CreateCollectionPropertyInstance(property, prop.PropertyType);

                    // allowAdd is false - we need to assign instance as the new property value
                    prop.SetValue(instance, collectionInstance, property.Name, false /* allowAdd? */);
                }
                else
                {
                    // Clear existing Collection
                    prop.ClearBackingICollectionInstance(collectionInstance);
                }

                bool isElementNullable = prop.EdmProperty.Type.AsCollection().ElementType().IsNullable;
                this.CollectionValueMaterializationPolicy.ApplyCollectionDataValues(
                    property,
                    collectionInstance,
                    prop.PrimitiveOrComplexCollectionItemType,
                    prop.AddValueToBackingICollectionInstance,
                    isElementNullable);
            }
            else if ((enumTypeTmp = Nullable.GetUnderlyingType(prop.NullablePropertyType) ?? prop.NullablePropertyType) != null &&
                     enumTypeTmp.IsEnum)
            {
                ODataEnumValue enumValue = property.Value as ODataEnumValue;
                object         tmpValue  = EnumValueMaterializationPolicy.MaterializeODataEnumValue(enumTypeTmp, enumValue);

                // TODO: 1. use EnumValueMaterializationPolicy 2. handle nullable enum property
                prop.SetValue(instance, tmpValue, property.Name, false /* allowAdd? */);
            }
            else
            {
                object            propertyValue = property.Value;
                ODataComplexValue complexValue  = propertyValue as ODataComplexValue;
                if (propertyValue != null && complexValue != null)
                {
                    if (!prop.EdmProperty.Type.IsComplex())
                    {
                        // The error message is a bit odd, but it's compatible with V1.
                        throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_ExpectingSimpleValue);
                    }

                    // Complex type.
                    bool needToSet = false;

                    ClientEdmModel       edmModel    = this.MaterializerContext.Model;
                    ClientTypeAnnotation complexType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(prop.PropertyType));
                    object complexInstance           = prop.GetValue(instance);

                    // Validating property inheritance in complexvalue and instance
                    if (prop.PropertyType.Name != property.Name)
                    {
                        complexType = this.MaterializerContext.ResolveTypeForMaterialization(prop.PropertyType, complexValue.TypeName);

                        // recreate complexInstance with derived type
                        complexInstance = null;
                    }

                    if (complexValue.Properties.Any() || complexInstance == null)
                    {
                        complexInstance = this.CreateNewInstance(complexType.EdmTypeReference, complexType.ElementType);
                        needToSet       = true;
                    }

                    this.MaterializeDataValues(complexType, complexValue.Properties, this.MaterializerContext.IgnoreMissingProperties);
                    this.ApplyDataValues(complexType, complexValue.Properties, complexInstance);

                    if (needToSet)
                    {
                        prop.SetValue(instance, complexInstance, property.Name, true /* allowAdd? */);
                    }
                }
                else
                {
                    this.MaterializePrimitiveDataValue(prop.NullablePropertyType, property);
                    prop.SetValue(instance, property.GetMaterializedValue(), property.Name, true /* allowAdd? */);
                }
            }
        }
Beispiel #17
0
        private void MaterializeResolvedEntry(MaterializerEntry entry, bool includeLinks)
        {
            ClientTypeAnnotation actualType = entry.ActualType;

            if (!actualType.IsEntityType)
            {
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.AtomMaterializer_InvalidNonEntityType(actualType.ElementTypeName));
            }
            ODataMaterializer.MaterializeDataValues(actualType, entry.Properties, base.ResponseInfo.IgnoreMissingProperties);
            if (entry.NavigationLinks != null)
            {
                foreach (ODataNavigationLink link in entry.NavigationLinks)
                {
                    ClientPropertyAnnotation annotation2 = actualType.GetProperty(link.Name, true);
                    if (annotation2 != null)
                    {
                        ValidatePropertyMatch(annotation2, link, base.ResponseInfo, true);
                    }
                }
            }
            if (includeLinks && (entry.NavigationLinks != null))
            {
                foreach (ODataNavigationLink link2 in entry.NavigationLinks)
                {
                    MaterializerNavigationLink link3 = MaterializerNavigationLink.GetLink(link2);
                    if (link3 != null)
                    {
                        ClientPropertyAnnotation annotation3 = actualType.GetProperty(link2.Name, base.ResponseInfo.IgnoreMissingProperties);
                        if (annotation3 != null)
                        {
                            if (link3.Feed != null)
                            {
                                this.ApplyFeedToCollection(entry, annotation3, link3.Feed, includeLinks);
                            }
                            else if (link3.Entry != null)
                            {
                                MaterializerEntry entry2 = link3.Entry;
                                if (entry2.Entry != null)
                                {
                                    this.Materialize(entry2, annotation3.PropertyType, includeLinks);
                                }
                                if (entry.ShouldUpdateFromPayload)
                                {
                                    annotation3.SetValue(entry.ResolvedObject, entry2.ResolvedObject, link2.Name, true);
                                    this.log.SetLink(entry, annotation3.PropertyName, entry2.ResolvedObject);
                                }
                            }
                        }
                    }
                }
            }
            foreach (ODataProperty property in entry.Properties)
            {
                if (!(property.Value is ODataStreamReferenceValue))
                {
                    ClientPropertyAnnotation annotation4 = actualType.GetProperty(property.Name, base.ResponseInfo.IgnoreMissingProperties);
                    if ((annotation4 != null) && entry.ShouldUpdateFromPayload)
                    {
                        ValidatePropertyMatch(annotation4, property, base.ResponseInfo, true);
                        ODataMaterializer.ApplyDataValue(actualType, property, base.ResponseInfo.IgnoreMissingProperties, base.ResponseInfo, entry.ResolvedObject);
                    }
                }
            }
            ApplyLinkProperties(actualType, entry);
            if (base.ResponseInfo.HasReadingEntityHandlers)
            {
                ODataMaterializer.ReadingEntityInfo annotation = entry.Entry.GetAnnotation <ODataMaterializer.ReadingEntityInfo>();
                base.ResponseInfo.FireReadingEntityEvent(entry.ResolvedObject, annotation.EntryPayload, annotation.BaseUri);
            }
        }