Exemple #1
0
        /// <summary>
        /// Computes the metadata version of the property.
        /// </summary>
        /// <param name="propertyCollection">List of properties for which metadata version needs to be computed.</param>
        /// <param name="visitedComplexTypes">List of complex type already visited.</param>
        /// <returns>the metadata version of the property collection.</returns>
        private Version ComputeVersionForPropertyCollection(IEnumerable <IEdmProperty> propertyCollection, HashSet <IEdmType> visitedComplexTypes)
        {
            Version propertyMetadataVersion = Util.ODataVersion4;

            foreach (IEdmProperty property in propertyCollection)
            {
                ClientPropertyAnnotation propertyAnnotation = this.model.GetClientPropertyAnnotation(property);

                if (property.Type.TypeKind() == EdmTypeKind.Complex && !propertyAnnotation.IsDictionary)
                {
                    if (visitedComplexTypes == null)
                    {
                        visitedComplexTypes = new HashSet <IEdmType>(EqualityComparer <IEdmType> .Default);
                    }
                    else if (visitedComplexTypes.Contains(property.Type.Definition))
                    {
                        continue;
                    }

                    visitedComplexTypes.Add(property.Type.Definition);

                    WebUtil.RaiseVersion(ref propertyMetadataVersion, this.ComputeVersionForPropertyCollection(this.model.GetClientTypeAnnotation(property).EdmProperties(), visitedComplexTypes));
                }
            }

            return(propertyMetadataVersion);
        }
Exemple #2
0
        /// <summary>
        /// Check if this type represents an ATOM-style media link entry and
        /// if so mark the ClientType as such
        /// </summary>
        private void CheckMediaLinkEntry()
        {
            this.isMediaLinkEntry = false;

            // MediaEntryAttribute does not allow multiples, so there can be at most 1 instance on the type.
            MediaEntryAttribute mediaEntryAttribute = (MediaEntryAttribute)this.ElementType.GetCustomAttributes(typeof(MediaEntryAttribute), true).SingleOrDefault();

            if (mediaEntryAttribute != null)
            {
                this.isMediaLinkEntry = true;

                ClientPropertyAnnotation mediaProperty = this.Properties().SingleOrDefault(p => p.PropertyName == mediaEntryAttribute.MediaMemberName);
                if (mediaProperty == null)
                {
                    throw Microsoft.OData.Client.Error.InvalidOperation(Microsoft.OData.Client.Strings.ClientType_MissingMediaEntryProperty(
                                                                            this.ElementTypeName, mediaEntryAttribute.MediaMemberName));
                }

                this.mediaDataMember = mediaProperty;
            }

            // HasStreamAttribute does not allow multiples, so there can be at most 1 instance on the type.
            bool hasStreamAttribute = this.ElementType.GetCustomAttributes(typeof(HasStreamAttribute), true).Any();

            if (hasStreamAttribute)
            {
                this.isMediaLinkEntry = true;
            }
        }
 public void Initialize()
 {
     this.clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4);
     this.clientEdmModel.GetOrCreateEdmType(typeof(TestCustomer));
     this.clientEdmModel.GetOrCreateEdmType(typeof(TestOrder));
     this.materializerContext = new TestMaterializerContext() { Model = this.clientEdmModel };
     this.ordersProperty = this.clientEdmModel.GetClientTypeAnnotation(typeof(TestCustomer)).GetProperty("Orders", false);
 }
Exemple #4
0
 /// <summary>
 /// Determines whether a given property should be serialized into an insert or update payload.
 /// </summary>
 /// <param name="type">The declaring type of the property.</param>
 /// <param name="property">The property.</param>
 /// <returns>Whether or not the property should be serialized.</returns>
 private static bool ShouldSerializeProperty(ClientTypeAnnotation type, ClientPropertyAnnotation property)
 {
     // don't write property if it is a dictionary
     // don't write mime data member or the mime type member for it
     // link properties need to be ignored
     return(!property.IsDictionary &&
            property != type.MediaDataMember &&
            !property.IsStreamLinkProperty &&
            (type.MediaDataMember == null || type.MediaDataMember.MimeTypeProperty != property));
 }
Exemple #5
0
 /// <summary>
 /// Determines whether a given property should be serialized into an insert or update payload.
 /// </summary>
 /// <param name="type">The declaring type of the property.</param>
 /// <param name="property">The property.</param>
 /// <returns>Whether or not the property should be serialized.</returns>
 private static bool ShouldSerializeProperty(ClientTypeAnnotation type, ClientPropertyAnnotation property)
 {
     // don't write property if it is a dictionary
     // don't write mime data member or the mime type member for it
     // link properties need to be ignored
     // don't write property if it is tagged with IgnoreClientProperty attribute
     return(!property.IsDictionary &&
            property != type.MediaDataMember &&
            !property.IsStreamLinkProperty &&
            (type.MediaDataMember == null || type.MediaDataMember.MimeTypeProperty != property) &&
            property.PropertyInfo.GetCustomAttributes(typeof(IgnoreClientPropertyAttribute)).Count() == 0);
 }
        /// <summary>
        /// Validates the specified <paramref name="property"/> matches 
        /// the parsed <paramref name="link"/>.
        /// </summary>
        /// <param name="property">Property as understood by the type system.</param>
        /// <param name="link">Property as parsed.</param>
        /// <param name="model">Client Model.</param>
        /// <param name="performEntityCheck">whether to do the entity check or not.</param>
        /// <returns>The type</returns>
        internal static Type ValidatePropertyMatch(ClientPropertyAnnotation property, ODataNavigationLink link, ClientEdmModel model, bool performEntityCheck)
        {
            Debug.Assert(property != null, "property != null");
            Debug.Assert(link != null, "link != null");

            Type propertyType = null;
            if (link.IsCollection.HasValue)
            {
                if (link.IsCollection.Value)
                {
                    // We need to fail if the payload states that the property is a navigation collection property
                    // and in the client, the property is not a collection property.
                    if (!property.IsEntityCollection)
                    {
                        throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(property.PropertyName));
                    }

                    propertyType = property.EntityCollectionItemType;
                }
                else
                {
                    if (property.IsEntityCollection)
                    {
                        throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(property.PropertyName));
                    }

                    propertyType = property.PropertyType;
                }
            }

            // If the server type and the client type does not match, we need to throw.
            // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa
            if (propertyType != null && performEntityCheck)
            {
                if (!ClientTypeUtil.TypeIsEntity(propertyType, model))
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(propertyType.ToString()));
                }
            }

            return propertyType;
        }
 /// <summary>
 /// Determines whether a given property should be serialized into an insert or update payload.
 /// </summary>
 /// <param name="type">The declaring type of the property.</param>
 /// <param name="property">The property.</param>
 /// <returns>Whether or not the property should be serialized.</returns>
 private static bool ShouldSerializeProperty(ClientTypeAnnotation type, ClientPropertyAnnotation property)
 {
     // don't write property if it is a dictionary
     // don't write mime data member or the mime type member for it
     // link properties need to be ignored
     return !property.IsDictionary
         && property != type.MediaDataMember
         && !property.IsStreamLinkProperty
         && (type.MediaDataMember == null || type.MediaDataMember.MimeTypeProperty != property);
 }
 /// <summary>
 /// Validates the specified <paramref name="property"/> matches 
 /// the parsed <paramref name="link"/>.
 /// </summary>
 /// <param name="property">Property as understood by the type system.</param>
 /// <param name="link">Property as parsed.</param>
 internal static void ValidatePropertyMatch(ClientPropertyAnnotation property, ODataNavigationLink link)
 {
     ValidatePropertyMatch(property, link, null, false /*performEntityCheck*/);
 }
 /// <summary>
 /// Validates the specified <paramref name="property"/> matches 
 /// the parsed <paramref name="atomProperty"/>.
 /// </summary>
 /// <param name="property">Property as understood by the type system.</param>
 /// <param name="atomProperty">Property as parsed.</param>
 internal static void ValidatePropertyMatch(ClientPropertyAnnotation property, ODataProperty atomProperty)
 {
     ValidatePropertyMatch(property, atomProperty, null, false /*performEntityCheck*/);
 }
        /// <summary>
        /// Gets or creates a collection property on the specified <paramref name="instance"/>.
        /// </summary>
        /// <param name="instance">Instance on which to get/create the collection.</param>
        /// <param name="property">Collection property on the <paramref name="instance"/>.</param>
        /// <param name="forLoadProperty">Is this collection being created for LoadProperty scenario.</param>
        /// <returns>
        /// The collection corresponding to the specified <paramref name="property"/>;
        /// never null.
        /// </returns>
        private object GetOrCreateCollectionProperty(object instance, ClientPropertyAnnotation property, bool forLoadProperty)
        {
            Debug.Assert(instance != null, "instance != null");
            Debug.Assert(property != null, "property != null");
            Debug.Assert(property.IsEntityCollection, "property.IsEntityCollection has to be true - otherwise property isn't a collection");

            // NOTE: in V1, we would have instantiated nested objects before setting them.
            object result;
            result = property.GetValue(instance);

            if (result == null)
            {
                Type collectionType = property.PropertyType;

                // For backward compatiblity we need to have different strategy of collection creation b/w
                // LoadProperty scenario versus regular collection creation scenario.
                if (forLoadProperty)
                {
                    if (BindingEntityInfo.IsDataServiceCollection(collectionType, this.MaterializerContext.Model))
                    {
                        Debug.Assert(WebUtil.GetDataServiceCollectionOfT(property.EntityCollectionItemType) != null, "DataServiceCollection<> must be available here.");

                        // new DataServiceCollection<property.EntityCollectionItemType>(null, TrackingMode.None)
                        result = Activator.CreateInstance(
                            WebUtil.GetDataServiceCollectionOfT(property.EntityCollectionItemType),
                            null,
                            TrackingMode.None);
                    }
                    else
                    {
                        // Try List<> first because that's what we did in V1/V2, but use the actual property type if it doesn't support List<>
                        Type listCollectionType = typeof(List<>).MakeGenericType(property.EntityCollectionItemType);
                        if (collectionType.IsAssignableFrom(listCollectionType))
                        {
                            collectionType = listCollectionType;
                        }

                        result = Activator.CreateInstance(collectionType);
                    }
                }
                else
                {
                    if (DSClient.PlatformHelper.IsInterface(collectionType))
                    {
                        collectionType = typeof(System.Collections.ObjectModel.Collection<>).MakeGenericType(property.EntityCollectionItemType);
                    }

                    result = this.CreateNewInstance(property.EdmProperty.Type, collectionType);
                }

                // Update the property value on the instance.
                property.SetValue(instance, result, property.PropertyName, false /* add */);
            }

            Debug.Assert(result != null, "result != null -- otherwise GetOrCreateCollectionProperty didn't fall back to creation");
            return result;
        }
        /// <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,
            ODataFeed 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.EntityCollectionItemType));

            IEnumerable<ODataEntry> entries = MaterializerFeed.GetFeed(feed).Entries;

            foreach (ODataEntry feedEntry in entries)
            {
                this.Materialize(MaterializerEntry.GetEntry(feedEntry), collectionType.ElementType, includeLinks);
            }

            ProjectionPlan continuationPlan = includeLinks ? 
                ODataEntityMaterializer.CreatePlanForDirectMaterialization(property.EntityCollectionItemType) : 
                ODataEntityMaterializer.CreatePlanForShallowMaterialization(property.EntityCollectionItemType);
            
            this.ApplyItemsToCollection(
                entry, 
                property, 
                entries.Select(e => MaterializerEntry.GetEntry(e).ResolvedObject), 
                feed.NextPageLink, 
                continuationPlan,
                false);
        }
        private MaterializeAtom ReadPropertyFromRawData(ClientPropertyAnnotation property)
        {
            DataServiceContext context = (DataServiceContext)this.Source;

            bool merging = context.ApplyingChanges;

            try
            {
                context.ApplyingChanges = true;

                // if this is the data property for a media entry, what comes back
                // is the raw value (no markup)
#if ASTORIA_OPEN_OBJECT
                object openProps = null;
#endif
                string mimeType = null;
                Encoding encoding = null;
                Type elementType = property.EntityCollectionItemType ?? property.NullablePropertyType;
                IList results = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType));
                ContentTypeUtil.ReadContentType(this.ContentType, out mimeType, out encoding);

                using (Stream responseStream = this.GetResponseStream())
                {
                    // special case byte[], and for everything else let std conversion kick-in
                    if (property.PropertyType == typeof(byte[]))
                    {
                        int total = checked((int)this.ContentLength);
                        byte[] buffer = null;
                        if (total >= 0)
                        {
                            buffer = LoadPropertyResult.ReadByteArrayWithContentLength(responseStream, total);
                        }
                        else
                        {
                            buffer = LoadPropertyResult.ReadByteArrayChunked(responseStream);
                        }

                        results.Add(buffer);
#if ASTORIA_OPEN_OBJECT
                            property.SetValue(this.entity, buffer, this.propertyName, ref openProps, false);
#else
                        property.SetValue(this.entity, buffer, this.propertyName, false);
#endif
                    }
                    else
                    {
                        // responseStream will disposed, StreamReader doesn't need to dispose of it.
                        StreamReader reader = new StreamReader(responseStream, encoding);
                        object convertedValue = property.PropertyType == typeof(string) ?
                                                    reader.ReadToEnd() :
                                                    ClientConvert.ChangeType(reader.ReadToEnd(), property.PropertyType);
                        results.Add(convertedValue);
#if ASTORIA_OPEN_OBJECT
                            property.SetValue(this.entity, convertedValue, this.propertyName, ref openProps, false);
#else
                        property.SetValue(this.entity, convertedValue, this.propertyName, false);
#endif
                    }
                }

#if ASTORIA_OPEN_OBJECT
                Debug.Assert(openProps == null, "These should not be set in this path");
#endif
                if (property.MimeTypeProperty != null)
                {
                    // an implication of this 3rd-arg-null is that mime type properties cannot be open props
#if ASTORIA_OPEN_OBJECT
                    property.MimeTypeProperty.SetValue(this.entity, mimeType, null, ref openProps, false);
                    Debug.Assert(openProps == null, "These should not be set in this path");
#else
                    property.MimeTypeProperty.SetValue(this.entity, mimeType, null, false);
#endif
                }

                return MaterializeAtom.CreateWrapper(context, results);
            }
            finally
            {
                context.ApplyingChanges = merging;
            }
        }
        /// <summary>
        /// Populates the collection property on the entry's resolved object with the given items enumerator.
        /// </summary>
        /// <param name="entry">Entry with collection to be modified.</param>
        /// <param name="property">Collection property on the entry.</param>
        /// <param name="items">Values to apply onto the collection.</param>
        /// <param name="nextLink">Next link for collection continuation.</param>
        /// <param name="continuationPlan">Projection plan for collection continuation.</param>
        /// <returns>Collection instance that was populated.</returns>
        private object PopulateCollectionProperty(
            MaterializerEntry entry,
            ClientPropertyAnnotation property,
            IEnumerable<object> items,
            Uri nextLink,
            ProjectionPlan continuationPlan)
        {
            Debug.Assert(entry.Entry != null || entry.ForLoadProperty, "ODataEntry should be non-null except for LoadProperty");
            Debug.Assert(property != null, "property != null");
            Debug.Assert(items != null, "items != null");

            object collection = null;

            ClientEdmModel edmModel = this.MaterializerContext.Model;
            ClientTypeAnnotation collectionType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(property.EntityCollectionItemType));

            if (entry.ShouldUpdateFromPayload)
            {
                collection = this.GetOrCreateCollectionProperty(entry.ResolvedObject, property, entry.ForLoadProperty);

                foreach (object item in items)
                {
                    // Validate that item can be inserted into collection.
                    ValidateCollectionElementTypeIsItemType(item.GetType(), collectionType.ElementType);

                    property.SetValue(collection, item, property.PropertyName, true /* allowAdd? */);

                    this.EntityTrackingAdapter.MaterializationLog.AddedLink(entry, property.PropertyName, item);
                }

                this.FoundNextLinkForCollection(collection as IEnumerable, nextLink, continuationPlan);
            }
            else
            {
                Debug.Assert(!entry.ForLoadProperty, "LoadProperty should always have ShouldUpateForPayload set to true.");

                foreach (object item in items)
                {
                    // Validate that item can be inserted into collection.
                    ValidateCollectionElementTypeIsItemType(item.GetType(), collectionType.ElementType);
                }

                this.FoundNextLinkForUnmodifiedCollection(property.GetValue(entry.ResolvedObject) as IEnumerable);
            }

            return collection;
        }
        /// <summary>
        /// Creates and returns an <see cref="ODataValue"/> for the given primitive value.
        /// </summary>
        /// <param name="property">The property being converted.</param>
        /// <param name="propertyValue">The property value to convert..</param>
        /// <returns>An ODataValue representing the given value.</returns>
        private static ODataValue CreateODataPrimitivePropertyValue(ClientPropertyAnnotation property, object propertyValue)
        {
            if (propertyValue == null)
            {
                return new ODataNullValue();
            }

            propertyValue = ConvertPrimitiveValueToRecognizedODataType(propertyValue, property.PropertyType);

            return new ODataPrimitiveValue(propertyValue);
        }
Exemple #15
0
 /// <summary>
 /// Sets the given instance of <paramref name="annotation"/> to the given instance of <paramref name="edmProperty"/>.
 /// </summary>
 /// <param name="edmProperty">IEdmProperty instance to set the annotation.</param>
 /// <param name="annotation">Annotation instance to set.</param>
 internal static void SetClientPropertyAnnotation(this IEdmProperty edmProperty, ClientPropertyAnnotation annotation)
 {
     Debug.Assert(edmProperty != null, "edmProperty != null");
     Debug.Assert(annotation != null, "annotation != null");
     annotation.Model.SetAnnotationValue <ClientPropertyAnnotation>(edmProperty, annotation);
 }
        /// <summary>
        /// Returns the value of the complex property.
        /// </summary>
        /// <param name="property">Property which contains name, type, is key (if false and null value, will throw).</param>
        /// <param name="propertyValue">property value</param>
        /// <param name="visitedComplexTypeObjects">List of instances of complex types encountered in the hierarchy. Used to detect cycles.</param>
        /// <returns>An instance of ODataComplexValue containing the value of the properties of the given complex type.</returns>
        private ODataComplexValue CreateODataComplexPropertyValue(ClientPropertyAnnotation property, object propertyValue, HashSet<object> visitedComplexTypeObjects)
        {
            Debug.Assert(propertyValue != null || !property.IsPrimitiveOrEnumOrComplexCollection, "Collection items must not be null");

            Type propertyType = property.IsPrimitiveOrEnumOrComplexCollection ? property.PrimitiveOrComplexCollectionItemType : property.PropertyType;
            if (propertyValue != null && propertyType != propertyValue.GetType())
            {
                Debug.Assert(propertyType.IsAssignableFrom(propertyValue.GetType()), "Type from value should equals to or derived from property type from model.");
                propertyType = propertyValue.GetType();
            }

            return this.CreateODataComplexValue(propertyType, propertyValue, property.PropertyName, property.IsPrimitiveOrEnumOrComplexCollection, visitedComplexTypeObjects);
        }
        /// <summary>
        /// Tries to convert the given value into an instance of <see cref="ODataValue"/>.
        /// </summary>
        /// <param name="property">The property being converted.</param>
        /// <param name="propertyValue">The property value to convert..</param>
        /// <param name="serverTypeName">The server type name of the entity whose properties are being populated.</param>
        /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param>
        /// <param name="odataValue">The odata value if one was created.</param>
        /// <returns>Whether or not the value was converted.</returns>
        private bool TryConvertPropertyValue(ClientPropertyAnnotation property, object propertyValue, string serverTypeName, HashSet<object> visitedComplexTypeObjects, out ODataValue odataValue)
        {
            if (property.IsKnownType)
            {
                odataValue = CreateODataPrimitivePropertyValue(property, propertyValue);
                return true;
            }

            if (property.IsEnumType)
            {
                string enumValue;
                if (propertyValue == null)
                {
                    enumValue = null;
                }
                else
                {
                    enumValue = ClientTypeUtil.GetEnumValuesString(propertyValue.ToString(), property.PropertyType);
                }

                string typeNameInMetadata = this.requestInfo.ResolveNameFromType(property.PropertyType);
                odataValue = new ODataEnumValue(enumValue, typeNameInMetadata);
                return true;
            }

            if (property.IsPrimitiveOrEnumOrComplexCollection)
            {
                odataValue = this.CreateODataCollectionPropertyValue(property, propertyValue, serverTypeName, visitedComplexTypeObjects);
                return true;
            }

            if (!property.IsEntityCollection && !ClientTypeUtil.TypeIsEntity(property.PropertyType, this.requestInfo.Model))
            {
                odataValue = this.CreateODataComplexPropertyValue(property, propertyValue, visitedComplexTypeObjects);
                return true;
            }

            odataValue = null;
            return false;
        }
 /// <summary>
 /// Returns the value of the collection property.
 /// </summary>
 /// <param name="property">Collection property details. Must not be null.</param>
 /// <param name="propertyValue">Collection instance.</param>
 /// <param name="serverTypeName">The server type name of the entity whose properties are being populated.</param>
 /// <param name="visitedComplexTypeObjects">List of instances of complex types encountered in the hierarchy. Used to detect cycles.</param>
 /// <returns>An instance of ODataCollectionValue representing the value of the property.</returns>
 private ODataCollectionValue CreateODataCollectionPropertyValue(ClientPropertyAnnotation property, object propertyValue, string serverTypeName, HashSet<object> visitedComplexTypeObjects)
 {
     Debug.Assert(property != null, "property != null");
     Debug.Assert(property.IsPrimitiveOrEnumOrComplexCollection, "This method is supposed to be used only for writing collections");
     Debug.Assert(property.PropertyName != null, "property.PropertyName != null");
     bool isDynamic = this.requestInfo.TypeResolver.ShouldWriteClientTypeForOpenServerProperty(property.EdmProperty, serverTypeName);
     return this.CreateODataCollection(property.PrimitiveOrComplexCollectionItemType, property.PropertyName, propertyValue, visitedComplexTypeObjects, isDynamic);
 }
Exemple #19
0
 /// <summary>
 /// Sets the given instance of <paramref name="annotation"/> to the given instance of <paramref name="edmProperty"/>.
 /// </summary>
 /// <param name="edmProperty">IEdmProperty instance to set the annotation.</param>
 /// <param name="annotation">Annotation instance to set.</param>
 internal static void SetClientPropertyAnnotation(this IEdmProperty edmProperty, ClientPropertyAnnotation annotation)
 {
     Debug.Assert(edmProperty != null, "edmProperty != null");
     Debug.Assert(annotation != null, "annotation != null");
     annotation.Model.SetAnnotationValue<ClientPropertyAnnotation>(edmProperty, annotation);
 }
 /// <summary>
 /// Builds an edm property value from the given annotation.
 /// </summary>
 /// <param name="propertyAnnotation">The property annotation.</param>
 /// <returns>The property value</returns>
 private IEdmPropertyValue BuildEdmPropertyValue(ClientPropertyAnnotation propertyAnnotation)
 {
     var propertyValue = propertyAnnotation.GetValue(this.structuredValue);
     var edmValue = this.ConvertToEdmValue(propertyValue, propertyAnnotation.EdmProperty.Type);
     return new EdmPropertyValue(propertyAnnotation.EdmProperty.Name, edmValue);
 }
Exemple #21
0
 /// <summary>
 /// Constructs a new instance.
 /// </summary>
 /// <param name="requestInfo">Information about the request.</param>
 /// <param name="mergeOption">Merge option.</param>
 /// <param name="entityDescriptor">Entity whose property is being loaded.</param>
 /// <param name="property">Property which is being loaded.</param>
 internal LoadPropertyResponseInfo(
     RequestInfo requestInfo, 
     MergeOption mergeOption, 
     EntityDescriptor entityDescriptor, 
     ClientPropertyAnnotation property)
     : base(requestInfo, mergeOption)
 {
     this.EntityDescriptor = entityDescriptor;
     this.Property = property;
 }
        /// <summary>
        /// Adds a type annotation to the value if it is primitive and not defined on the server.
        /// </summary>
        /// <param name="serverTypeName">The server type name of the entity whose properties are being populated.</param>
        /// <param name="property">The current property.</param>
        /// <param name="odataValue">The already converted value of the property.</param>
        private void AddTypeAnnotationNotDeclaredOnServer(string serverTypeName, ClientPropertyAnnotation property, ODataValue odataValue)
        {
            Debug.Assert(property != null, "property != null");
            Debug.Assert(property.EdmProperty != null, "property.EdmProperty != null");
            var primitiveValue = odataValue as ODataPrimitiveValue;
            if (primitiveValue == null)
            {
                return;
            }

            if (!this.requestInfo.Format.UsingAtom
                && this.requestInfo.TypeResolver.ShouldWriteClientTypeForOpenServerProperty(property.EdmProperty, serverTypeName)
                && !JsonSharedUtils.ValueTypeMatchesJsonType(primitiveValue, property.EdmProperty.Type.AsPrimitive()))
            {
                // DEVNOTE: it is safe to use the property type name for primitive types because they do not generally support inheritance,
                // and spatial values always contain their specific type inside the GeoJSON/GML representation.
                primitiveValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = property.EdmProperty.Type.FullName() });
            }
        }
Exemple #23
0
 /// <summary>
 /// Returns the instance of LoadPropertyResponseInfo class, which provides information for LoadProperty response handling.
 /// </summary>
 /// <param name="mergeOption">Merge option to use for conflict handling.</param>
 /// <param name="entityDescriptor">Entity whose property is being loaded.</param>
 /// <param name="property">Property which is being loaded.</param>
 /// <returns>Instance of the LoadPropertyResponseInfo class.</returns>
 internal ResponseInfo GetDeserializationInfoForLoadProperty(MergeOption? mergeOption, EntityDescriptor entityDescriptor, ClientPropertyAnnotation property)
 {
     return new LoadPropertyResponseInfo(
         this,
         mergeOption.HasValue ? mergeOption.Value : this.Context.MergeOption,
         entityDescriptor,
         property);
 }
        /// <summary>
        /// Check if this type represents an ATOM-style media link entry and
        /// if so mark the ClientType as such
        /// </summary>
        private void CheckMediaLinkEntry()
        {
            this.isMediaLinkEntry = false;

            // MediaEntryAttribute does not allow multiples, so there can be at most 1 instance on the type.
            MediaEntryAttribute mediaEntryAttribute = (MediaEntryAttribute)this.ElementType.GetCustomAttributes(typeof(MediaEntryAttribute), true).SingleOrDefault();
            if (mediaEntryAttribute != null)
            {
                this.isMediaLinkEntry = true;

                ClientPropertyAnnotation mediaProperty = this.Properties().SingleOrDefault(p => p.PropertyName == mediaEntryAttribute.MediaMemberName);
                if (mediaProperty == null)
                {
                    throw Microsoft.OData.Client.Error.InvalidOperation(Microsoft.OData.Client.Strings.ClientType_MissingMediaEntryProperty(
                        this.ElementTypeName, mediaEntryAttribute.MediaMemberName));
                }

                this.mediaDataMember = mediaProperty;
            }

            // HasStreamAttribute does not allow multiples, so there can be at most 1 instance on the type.
            bool hasStreamAttribute = this.ElementType.GetCustomAttributes(typeof(HasStreamAttribute), true).Any();
            if (hasStreamAttribute)
            {
                this.isMediaLinkEntry = true;
            }
        }
        /// <summary>
        /// Try and get the navigation link. If the navigation link is not specified, then its used the self link of the entity and
        /// appends the property name.
        /// </summary>
        /// <param name="baseUriResolver">retrieves the appropriate baseUri for a given entitySet.</param>
        /// <param name="property">ClientProperty instance representing the navigation property.</param>
        /// <returns>returns the uri for the given link. If the link is not present, its uses the self link of the current entity and appends the navigation property name.</returns>
        internal Uri GetNavigationLink(UriResolver baseUriResolver, ClientPropertyAnnotation property)
        {
            LinkInfo linkInfo = null;
            Uri uri = null;
            if (this.TryGetLinkInfo(property.PropertyName, out linkInfo))
            {
                uri = linkInfo.NavigationLink;
            }

            if (uri == null)
            {
                Uri relativeUri = UriUtil.CreateUri(property.PropertyName, UriKind.Relative); 
                uri = UriUtil.CreateUri(this.GetResourceUri(baseUriResolver, true /*queryLink*/), relativeUri);
            }

            return uri;
        }
        /// <summary>
        /// Validates the specified <paramref name="property"/> matches 
        /// the parsed <paramref name="atomProperty"/>.
        /// </summary>
        /// <param name="property">Property as understood by the type system.</param>
        /// <param name="atomProperty">Property as parsed.</param>
        /// <param name="model">Client model.</param>
        /// <param name="performEntityCheck">whether to do the entity check or not.</param>
        internal static void ValidatePropertyMatch(ClientPropertyAnnotation property, ODataProperty atomProperty, ClientEdmModel model, bool performEntityCheck)
        {
            Debug.Assert(property != null, "property != null");
            Debug.Assert(atomProperty != null, "atomProperty != null");

            ODataFeed feed = atomProperty.Value as ODataFeed;
            ODataEntry entry = atomProperty.Value as ODataEntry;

            if (property.IsKnownType && (feed != null || entry != null))
            {
                throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkLocalSimple);
            }

            Type propertyType = null;
            if (feed != null)
            {
                // We need to fail if the payload states that the property is a navigation collection property
                // and in the client, the property is not a collection property.
                if (!property.IsEntityCollection)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(property.PropertyName));
                }

                propertyType = property.EntityCollectionItemType;
            }

            if (entry != null)
            {
                if (property.IsEntityCollection)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(property.PropertyName));
                }

                propertyType = property.PropertyType;
            }

            // If the server type and the client type does not match, we need to throw.
            // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa
            if (propertyType != null && performEntityCheck)
            {
                if (!ClientTypeUtil.TypeIsEntity(propertyType, model))
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(propertyType.ToString()));
                }
            }
        }
        /// <summary>
        /// Tries to get the value of a property and corresponding BindingPropertyInfo or ClientPropertyAnnotation if the property exists
        /// </summary>
        /// <param name="source">Source object whose property needs to be read</param>
        /// <param name="sourceProperty">Name of the source object property</param>
        /// <param name="model">The client model.</param>
        /// <param name="bindingPropertyInfo">BindingPropertyInfo corresponding to <paramref name="sourceProperty"/></param>
        /// <param name="clientProperty">Instance of ClientProperty corresponding to <paramref name="sourceProperty"/></param>
        /// <param name="propertyValue">Value of the property</param>
        /// <returns>true if the property exists and the value was read; otherwise false.</returns>
        internal static bool TryGetPropertyValue(object source, string sourceProperty, ClientEdmModel model, out BindingPropertyInfo bindingPropertyInfo, out ClientPropertyAnnotation clientProperty, out object propertyValue)
        {
            Type sourceType = source.GetType();

            bindingPropertyInfo = BindingEntityInfo.GetObservableProperties(sourceType, model)
                                                   .SingleOrDefault(x => x.PropertyInfo.PropertyName == sourceProperty);

            bool propertyFound = bindingPropertyInfo != null;

            // bindingPropertyInfo is null for primitive properties.
            if (!propertyFound)
            {
                clientProperty = BindingEntityInfo.GetClientType(sourceType, model)
                    .GetProperty(sourceProperty, true);

                propertyFound = clientProperty != null;
                if (!propertyFound)
                {
                    propertyValue = null;
                }
                else
                {
                    propertyValue = clientProperty.GetValue(source);
                }
            }
            else
            {
                clientProperty = null;
                propertyValue = bindingPropertyInfo.PropertyInfo.GetValue(source);
            }

            return propertyFound;
        }
        /// <summary>
        /// Applies the values of the <paramref name="items"/> enumeration to the
        /// <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="items">Values to apply onto the collection.</param>
        /// <param name="nextLink">Next link for collection continuation.</param>
        /// <param name="continuationPlan">Projection plan for collection continuation.</param>
        /// <param name="isContinuation">Whether this is a continuation request.</param>
        internal void ApplyItemsToCollection(
            MaterializerEntry entry,
            ClientPropertyAnnotation property,
            IEnumerable items,
            Uri nextLink,
            ProjectionPlan continuationPlan,
            bool isContinuation)
        {
            Debug.Assert(entry.Entry != null || entry.ForLoadProperty, "ODataEntry should be non-null except for LoadProperty");
            Debug.Assert(property != null, "property != null");
            Debug.Assert(items != null, "items != null");

            IEnumerable<object> itemsEnumerable = ODataEntityMaterializer.EnumerateAsElementType<object>(items);

            // Populate the collection property with items collection.
            object collection = this.PopulateCollectionProperty(entry, property, itemsEnumerable, nextLink, continuationPlan);

            // Get collection of all non-linked elements in collection and remove them except for the ones that were obtained from the response.
            if (this.EntityTrackingAdapter.MergeOption == MergeOption.OverwriteChanges || 
                this.EntityTrackingAdapter.MergeOption == MergeOption.PreserveChanges)
            {
                var linkedItemsInCollection =
                    this.EntityTrackingAdapter
                        .EntityTracker
                        .GetLinks(entry.ResolvedObject, property.PropertyName)
                        .Select(l => new { l.Target, l.IsModified });

                if (collection != null && !property.IsDictionary)
                {
                    var nonLinkedNonReceivedItemsInCollection = ODataEntityMaterializer.EnumerateAsElementType<object>((IEnumerable)collection)
                        .Except(linkedItemsInCollection.Select(i => i.Target))
                        .Except(itemsEnumerable).ToArray();

                    // Since no link exists, we just remove the item from the collection
                    foreach (var item in nonLinkedNonReceivedItemsInCollection)
                    {
                        property.RemoveValue(collection, item);
                    }
                }

                // When the first time a property or collection is being loaded, we remove all items other than the ones that we receive.
                if (!isContinuation)
                {
                    IEnumerable<object> itemsToRemove;
                    
                    if (this.EntityTrackingAdapter.MergeOption == MergeOption.OverwriteChanges)
                    {
                        itemsToRemove = linkedItemsInCollection.Select(i => i.Target);
                    }
                    else
                    {
                        Debug.Assert(
                            this.EntityTrackingAdapter.MergeOption == MergeOption.PreserveChanges, 
                            "this.EntityTrackingAdapter.MergeOption == MergeOption.PreserveChanges");

                        itemsToRemove = linkedItemsInCollection
                            .Where(i => !i.IsModified)
                            .Select(i => i.Target);
                    }

                    itemsToRemove = itemsToRemove.Except(itemsEnumerable);

                    foreach (var item in itemsToRemove)
                    {
                        if (collection != null)
                        {
                            property.RemoveValue(collection, item);
                        }

                        this.EntityTrackingAdapter.MaterializationLog.RemovedLink(entry, property.PropertyName, item);
                    }
                }
            }
        }
Exemple #29
0
        /// <summary>
        /// Creates an ODataEntry using some properties extracted from an entity operation parameter.
        /// </summary>
        /// <param name="clientTypeAnnotation">The client type annotation of the entity.</param>
        /// <param name="parameterValue">The Clr value of the entity.</param>
        /// <returns>The ODataEntry created.</returns>
        private ODataEntry CreateODataEntryFromEntityOperationParameter(ClientTypeAnnotation clientTypeAnnotation, object parameterValue)
        {
            ClientPropertyAnnotation[] properties = new ClientPropertyAnnotation[0];
            if (sendOption == EntityParameterSendOption.SendOnlySetProperties)
            {
                try
                {
                    var descripter = this.requestInfo.Context.EntityTracker.GetEntityDescriptor(parameterValue);
                    properties = clientTypeAnnotation.PropertiesToSerialize().Where(p => descripter.PropertiesToSerialize.Contains(p.PropertyName)).ToArray();
                }
                catch (InvalidOperationException)
                {
                    throw Error.InvalidOperation(Strings.Context_MustBeUsedWith("EntityParameterSendOption.SendOnlySetProperties", "DataServiceCollection"));
                }
            }

            return this.propertyConverter.CreateODataEntry(clientTypeAnnotation.ElementType, parameterValue, properties);
        }
        private MaterializeAtom ReadPropertyFromAtom(ClientPropertyAnnotation property)
        {
            DataServiceContext context = (DataServiceContext)this.Source;
            bool merging = context.ApplyingChanges;

            try
            {
                context.ApplyingChanges = true;

                // store the results so that they can be there in the response body.
                Type elementType = property.IsEntityCollection ? property.EntityCollectionItemType : property.NullablePropertyType;
                IList results = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType));

                DataServiceQueryContinuation continuation = null;

                // elementType.ElementType has Nullable stripped away, use nestedType for materializer
                using (MaterializeAtom materializer = this.GetMaterializer(this.plan))
                {
                    Debug.Assert(materializer != null, "materializer != null -- otherwise GetMaterializer() returned null rather than empty");

#if ASTORIA_OPEN_OBJECT
                     object openProperties = null;
#endif
                    // when SetLink to null, we cannot get materializer because have no-content response.
                    if (materializer.IsNoContentResponse() 
                        && property.GetValue(entity) != null 
                        && context.MergeOption != MergeOption.AppendOnly 
                        && context.MergeOption != MergeOption.NoTracking)
                    {
                        property.SetValue(this.entity, null, propertyName, false);
                    }
                    else
                    {
                        foreach (object child in materializer)
                        {
                            if (property.IsEntityCollection)
                            {
                                results.Add(child);
                            }
                            else if (property.IsPrimitiveOrEnumOrComplexCollection)
                            {
                                Debug.Assert(property.PropertyType.IsAssignableFrom(child.GetType()), "Created instance for storing collection items has to be compatible with the actual one.");

                                // Collection materialization rules requires to clear the collection if not null or set the property first and then add the collection items
                                object collectionInstance = property.GetValue(this.entity);
                                if (collectionInstance == null)
                                {
                                    // type of child has been resolved as per rules for collections so it is the correct type to instantiate
                                    collectionInstance = Activator.CreateInstance(child.GetType());

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

                                foreach (var collectionItem in (IEnumerable)child)
                                {
                                    Debug.Assert(property.PrimitiveOrComplexCollectionItemType.IsAssignableFrom(collectionItem.GetType()), "Type of materialized collection items have to be compatible with the type of collection items in the actual collection property.");
                                    property.AddValueToBackingICollectionInstance(collectionInstance, collectionItem);
                                }

                                results.Add(collectionInstance);
                            }
                            else
                            {
#if ASTORIA_OPEN_OBJECT
                                property.SetValue(this.entity, child, this.propertyName, ref openProperties, false);
#else
                                // it is either primitive type, complex type or 1..1 navigation property so we just allow setting the value but not adding. 
                                property.SetValue(this.entity, child, this.propertyName, false);
                                results.Add(child);
#endif
                            }
                        }
                    }

                    continuation = materializer.GetContinuation(null);
                }

                return MaterializeAtom.CreateWrapper(context, results, continuation);
            }
            finally
            {
                context.ApplyingChanges = merging;
            }
        }