/// <summary> /// Applies the collection data values to a collection instance. /// </summary> /// <param name="items">The items.</param> /// <param name="wireTypeName">Name of the wire type.</param> /// <param name="collectionInstance">The collection instance.</param> /// <param name="collectionItemType">Type of the collection item.</param> /// <param name="addValueToBackingICollectionInstance">The add value to backing I collection instance.</param> /// <param name="isElementNullable">If element type is nullable.</param> internal void ApplyCollectionDataValues( IEnumerable items, string wireTypeName, object collectionInstance, Type collectionItemType, Action <object, object> addValueToBackingICollectionInstance, bool isElementNullable) { Debug.Assert(collectionInstance != null, "collectionInstance != null"); Debug.Assert(WebUtil.IsCLRTypeCollection(collectionInstance.GetType(), this.materializerContext.Model), "collectionInstance must be a CollectionValue"); Debug.Assert(collectionItemType.IsAssignableFrom( ClientTypeUtil.GetImplementationType(collectionInstance.GetType(), typeof(ICollection <>)).GetGenericArguments()[0]), "collectionItemType has to match the collectionInstance generic type."); Debug.Assert(!ClientTypeUtil.TypeIsEntity(collectionItemType, this.materializerContext.Model), "CollectionValues cannot contain entities"); Debug.Assert(addValueToBackingICollectionInstance != null, "AddValueToBackingICollectionInstance != null"); // is the Collection not empty ? if (items != null) { bool isCollectionItemTypePrimitive = PrimitiveType.IsKnownNullableType(collectionItemType); foreach (object item in items) { if (!isElementNullable && item == null) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Collection_NullCollectionItemsNotSupported); } ODataEnumValue enumVal = null; // Is it a Collection of primitive types? if (isCollectionItemTypePrimitive) { if (item is ODataCollectionValue) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Collection_CollectionTypesInCollectionOfPrimitiveTypesNotAllowed); } object materializedValue = this.primitiveValueMaterializationPolicy.MaterializePrimitiveDataValueCollectionElement(collectionItemType, wireTypeName, item); addValueToBackingICollectionInstance(collectionInstance, materializedValue); } else if ((enumVal = item as ODataEnumValue) != null) { // TODO: use EnumValueMaterializationPolicy.MaterializeEnumDataValueCollectionElement() here object tmpValue = EnumValueMaterializationPolicy.MaterializeODataEnumValue(collectionItemType, enumVal); addValueToBackingICollectionInstance(collectionInstance, tmpValue); } else { if (item != null) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Collection_PrimitiveTypesInCollectionOfComplexTypesNotAllowed); } addValueToBackingICollectionInstance(collectionInstance, null); } } } }
/// <summary> /// Convert an instance annotation to clr object. /// </summary> /// <param name="instanceAnnotation">Instance annotation to be converted</param> /// <param name="clrInstanceAnnotation">The clr object</param> /// <returns>A dictionary of clr-typed instance annotation</returns> private bool TryConvertToClrInstanceAnnotation(ODataInstanceAnnotation instanceAnnotation, out object clrInstanceAnnotation) { clrInstanceAnnotation = null; var primitiveValue = instanceAnnotation.Value as ODataPrimitiveValue; if (primitiveValue != null) { clrInstanceAnnotation = primitiveValue.Value; return(true); } var enumValue = instanceAnnotation.Value as ODataEnumValue; if (enumValue != null) { var type = this.MaterializerContext.Context.ResolveTypeFromName(enumValue.TypeName); if (type != null) { clrInstanceAnnotation = EnumValueMaterializationPolicy.MaterializeODataEnumValue(type, enumValue); return(true); } return(false); } var collectionValue = instanceAnnotation.Value as ODataCollectionValue; if (collectionValue != null) { var serverSideModel = this.MaterializerContext.Context.Format.LoadServiceModel(); var valueTerm = serverSideModel.FindTerm(instanceAnnotation.Name); if (valueTerm != null && valueTerm.Type != null && valueTerm.Type.Definition != null) { var edmCollectionType = valueTerm.Type.Definition as IEdmCollectionType; if (edmCollectionType != null) { Type collectionItemType = null; var elementType = edmCollectionType.ElementType; PrimitiveType primitiveType; if (PrimitiveType.TryGetPrimitiveType(elementType.FullName(), out primitiveType)) { collectionItemType = primitiveType.ClrType; } else { collectionItemType = this.MaterializerContext.Context.ResolveTypeFromName(elementType.FullName()); } if (collectionItemType != null) { Type collectionICollectionType = typeof(ICollection <>).MakeGenericType(new Type[] { collectionItemType }); ClientTypeAnnotation collectionClientTypeAnnotation = this.MaterializerContext.ResolveTypeForMaterialization( collectionICollectionType, collectionValue.TypeName); bool isElementNullable = edmCollectionType.ElementType.IsNullable; var collectionInstance = this.CollectionValueMaterializationPolicy.CreateCollectionInstance( collectionClientTypeAnnotation.EdmTypeReference as IEdmCollectionTypeReference, collectionClientTypeAnnotation.ElementType); this.CollectionValueMaterializationPolicy.ApplyCollectionDataValues( collectionValue.Items, collectionValue.TypeName, collectionInstance, collectionItemType, ClientTypeUtil.GetAddToCollectionDelegate(collectionICollectionType), isElementNullable); clrInstanceAnnotation = collectionInstance; return(true); } } } return(false); } var nullValue = instanceAnnotation.Value as ODataNullValue; if (nullValue != null) { clrInstanceAnnotation = null; return(true); } return(false); }
/// <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); } }