/// <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)); }
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(); } }
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); } } }
/// <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); } }
/// <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))); }
/// <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; } }
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); }
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); } }
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); } } } }
/// <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); }
/// <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); }
/// <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(); } }
/// <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? */); } } }
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); } }