/// <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, ODataNestedResourceInfo 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 or a complex collection property // and in the client, the property is not a collection property. if (!property.IsResourceSet) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(property.PropertyName)); } propertyType = property.ResourceSetItemType; } else { if (property.IsResourceSet) { 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.TypeIsStructured(propertyType, model)) { throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(propertyType.ToString())); } } return(propertyType); }
/// <summary> /// Gets the type that of the instances that will be returned by materializer. /// </summary> /// <param name="expectingPrimitiveValue">Whether the expected is a primitive type.</param> /// <param name="elementType">Actual type on the client.</param> /// <param name="model">The client model used.</param> /// <param name="implementationType">The actual type that implements ICollection<></param> /// <returns>Type of the instances that will be returned by materializer.</returns> /// <remarks> /// For collection navigation properties (i.e. ICollection<T> where T is an entity the method returns T. Materializer will /// return single entity each time MoveNext() is called. However for collection properties we need the whole property instead of just a /// single collection item. /// </remarks> private static Type GetTypeForMaterializer(bool expectingPrimitiveValue, Type elementType, ClientEdmModel model, out Type implementationType) { if (!expectingPrimitiveValue && typeof(IEnumerable).IsAssignableFrom(elementType)) { implementationType = ClientTypeUtil.GetImplementationType(elementType, typeof(ICollection <>)); if (implementationType != null) { Type expectedType = implementationType.GetGenericArguments()[0]; // already know its ICollection<> // We should use the inner type only if this is a collection of entities (as opposed to a collection of primitive or complex types) if (ClientTypeUtil.TypeIsStructured(expectedType, model)) { return(expectedType); } } } implementationType = null; return(elementType); }