/// <summary> /// Creates and returns an ODataResourceWrapper from the given value. /// </summary> /// <param name="complexType">The value type.</param> /// <param name="instance">The complex instance.</param> /// <param name="propertyName">If the value is a property, then it represents the name of the property. Can be null, for non-property.</param> /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param> /// <returns>An ODataResourceWrapper representing the given value.</returns> internal ODataResourceWrapper CreateODataResourceWrapperForComplex(Type complexType, object instance, string propertyName, HashSet <object> visitedComplexTypeObjects) { Debug.Assert(complexType != null, "complexType != null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation complexTypeAnnotation = model.GetClientTypeAnnotation(complexType); Debug.Assert(complexTypeAnnotation != null, "complexTypeAnnotation != null"); Debug.Assert(!complexTypeAnnotation.IsEntityType, "Unexpected entity"); if (instance == null) { return(new ODataResourceWrapper() { Resource = null }); } if (visitedComplexTypeObjects == null) { visitedComplexTypeObjects = new HashSet <object>(ReferenceEqualityComparer <object> .Instance); } else if (visitedComplexTypeObjects.Contains(instance)) { if (propertyName != null) { throw Error.InvalidOperation(Strings.Serializer_LoopsNotAllowedInComplexTypes(propertyName)); } else { Debug.Assert(complexTypeAnnotation.ElementTypeName != null, "complexTypeAnnotation.ElementTypeName != null"); throw Error.InvalidOperation(Strings.Serializer_LoopsNotAllowedInNonPropertyComplexTypes(complexTypeAnnotation.ElementTypeName)); } } visitedComplexTypeObjects.Add(instance); ODataResource resource = new ODataResource() { TypeName = complexTypeAnnotation.ElementTypeName }; string serverTypeName = this.requestInfo.GetServerTypeName(complexTypeAnnotation); resource.TypeAnnotation = new ODataTypeAnnotation(serverTypeName); resource.Properties = this.PopulateProperties(instance, serverTypeName, complexTypeAnnotation.PropertiesToSerialize(), visitedComplexTypeObjects); var wrapper = new ODataResourceWrapper() { Resource = resource, Instance = instance }; wrapper.NestedResourceInfoWrappers = this.PopulateNestedComplexProperties(instance, serverTypeName, complexTypeAnnotation.PropertiesToSerialize(), visitedComplexTypeObjects); visitedComplexTypeObjects.Remove(instance); return(wrapper); }
/// <summary> /// Creates and returns an ODataComplexValue from the given value. /// </summary> /// <param name="complexType">The value type.</param> /// <param name="value">The complex value.</param> /// <param name="propertyName">If the value is a property, then it represents the name of the property. Can be null, for non-property.</param> /// <param name="isCollectionItem">True, if the value is an item in a collection, false otherwise.</param> /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param> /// <returns>An ODataComplexValue representing the given value.</returns> internal ODataComplexValue CreateODataComplexValue(Type complexType, object value, string propertyName, bool isCollectionItem, HashSet <object> visitedComplexTypeObjects) { Debug.Assert(complexType != null, "complexType != null"); Debug.Assert(value != null || !isCollectionItem, "Collection items must not be null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation complexTypeAnnotation = model.GetClientTypeAnnotation(complexType); Debug.Assert(complexTypeAnnotation != null, "complexTypeAnnotation != null"); Debug.Assert(!complexTypeAnnotation.IsEntityType, "Unexpected entity"); // Handle null values for complex types by putting m:null="true" if (value == null) { Debug.Assert(!isCollectionItem, "Null collection items are not supported. Should have already been checked."); return(null); } if (visitedComplexTypeObjects == null) { visitedComplexTypeObjects = new HashSet <object>(ReferenceEqualityComparer <object> .Instance); } else if (visitedComplexTypeObjects.Contains(value)) { if (propertyName != null) { throw Error.InvalidOperation(Strings.Serializer_LoopsNotAllowedInComplexTypes(propertyName)); } else { Debug.Assert(complexTypeAnnotation.ElementTypeName != null, "complexTypeAnnotation.ElementTypeName != null"); throw Error.InvalidOperation(Strings.Serializer_LoopsNotAllowedInNonPropertyComplexTypes(complexTypeAnnotation.ElementTypeName)); } } visitedComplexTypeObjects.Add(value); ODataComplexValue odataComplexValue = new ODataComplexValue(); // When TypeName is set, it causes validation to occur when ODataLib writes out the collection. Part of the validation ensures that all items // in the collection are exactly the same type, no derived types are allowed. In the released WCF Data Services 5.0 implementation, we don't set // TypeName here, so that validation does not occur, therefore we will set this value only for JSON Light, so we don't break existing code. if (!this.requestInfo.Format.UsingAtom) { odataComplexValue.TypeName = complexTypeAnnotation.ElementTypeName; } // If this complex type is a collection item don't put type name on each item if (!isCollectionItem) { odataComplexValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = this.requestInfo.GetServerTypeName(complexTypeAnnotation) }); } odataComplexValue.Properties = this.PopulateProperties(value, complexTypeAnnotation.PropertiesToSerialize(), visitedComplexTypeObjects); visitedComplexTypeObjects.Remove(value); return(odataComplexValue); }
/// <summary> /// Write the entry element. /// </summary> /// <param name="entityDescriptor">The entity.</param> /// <param name="relatedLinks">Collection of links related to the entity.</param> /// <param name="requestMessage">The OData request message.</param> internal void WriteEntry(EntityDescriptor entityDescriptor, IEnumerable <LinkDescriptor> relatedLinks, ODataRequestMessageWrapper requestMessage) { ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation entityType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType())); using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/)) { ODataWriterWrapper entryWriter = ODataWriterWrapper.CreateForEntry(messageWriter, this.requestInfo.Configurations.RequestPipeline); // Get the server type name using the type resolver or from the entity descriptor string serverTypeName = this.requestInfo.GetServerTypeName(entityDescriptor); var entry = CreateODataEntry(entityDescriptor, serverTypeName, entityType, this.requestInfo.Format); if (serverTypeName == null) { serverTypeName = this.requestInfo.InferServerTypeNameFromServerModel(entityDescriptor); } IEnumerable <ClientPropertyAnnotation> properties; if ((!Util.IsFlagSet(this.options, SaveChangesOptions.ReplaceOnUpdate) && entityDescriptor.State == EntityStates.Modified && entityDescriptor.PropertiesToSerialize.Any()) || (Util.IsFlagSet(this.options, SaveChangesOptions.PostOnlySetProperties) && entityDescriptor.State == EntityStates.Added)) { properties = entityType.PropertiesToSerialize().Where(prop => entityDescriptor.PropertiesToSerialize.Contains(prop.PropertyName)); } else { properties = entityType.PropertiesToSerialize(); } entry.Properties = this.propertyConverter.PopulateProperties(entityDescriptor.Entity, serverTypeName, properties); entryWriter.WriteStart(entry, entityDescriptor.Entity); this.WriteNestedComplexProperties(entityDescriptor.Entity, serverTypeName, properties, entryWriter); if (EntityStates.Added == entityDescriptor.State) { this.WriteNestedResourceInfo(entityDescriptor, relatedLinks, entryWriter); } entryWriter.WriteEnd(entry, entityDescriptor.Entity); } }
/// <summary> /// Creates an ODataResource 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 ODataResource created.</returns> private ODataResourceWrapper CreateODataResourceFromEntityOperationParameter(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.CreateODataResourceWrapper(clientTypeAnnotation.ElementType, parameterValue, properties)); }
/// <summary> /// Creates and returns an ODataResourceWrapper from the given value. /// </summary> /// <param name="entityType">The value type.</param> /// <param name="value">The resource value.</param> /// <param name="properties">The given properties to serialize.</param> /// <returns>An ODataResourceWrapper representing the given value.</returns> internal ODataResourceWrapper CreateODataResourceWrapper(Type entityType, object value, params ClientPropertyAnnotation[] properties) { Debug.Assert(entityType != null, "entityType != null"); if (value == null) { return(null); } ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation entityTypeAnnotation = model.GetClientTypeAnnotation(value.GetType()); Debug.Assert(entityTypeAnnotation != null, "entityTypeAnnotation != null"); Debug.Assert(entityTypeAnnotation.IsStructuredType, "Unexpected type"); ODataResource odataEntityValue = new ODataResource() { TypeName = entityTypeAnnotation.ElementTypeName, }; string serverTypeName = this.requestInfo.GetServerTypeName(entityTypeAnnotation); odataEntityValue.TypeAnnotation = new ODataTypeAnnotation(serverTypeName); odataEntityValue.Properties = this.PopulateProperties(value, serverTypeName, properties.Any() ? properties : entityTypeAnnotation.PropertiesToSerialize(), null); var wrapper = new ODataResourceWrapper() { Resource = odataEntityValue }; wrapper.NestedResourceInfoWrappers = PopulateNestedComplexProperties(value, serverTypeName, properties.Any() ? properties : entityTypeAnnotation.PropertiesToSerialize(), null); return(wrapper); }
/// <summary> /// Write the entry element. /// </summary> /// <param name="entityDescriptor">The entity.</param> /// <param name="relatedLinks">Collection of links related to the entity.</param> /// <param name="requestMessage">The OData request message.</param> internal void WriteEntry(EntityDescriptor entityDescriptor, IEnumerable <LinkDescriptor> relatedLinks, ODataRequestMessageWrapper requestMessage) { ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation entityType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType())); using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/)) { ODataWriterWrapper entryWriter = ODataWriterWrapper.CreateForEntry(messageWriter, this.requestInfo.Configurations.RequestPipeline); // Get the server type name using the type resolver or from the entity descriptor string serverTypeName = this.requestInfo.GetServerTypeName(entityDescriptor); var entry = CreateODataEntry(entityDescriptor, serverTypeName, entityType, this.requestInfo.Format); // Add the annotation required for writing the payload into an XElement // for firing WritingEntity events if (this.requestInfo.HasWritingEventHandlers) { entry.SetAnnotation(new WritingEntityInfo(entityDescriptor.Entity, this.requestInfo)); } if (serverTypeName == null) { serverTypeName = this.requestInfo.InferServerTypeNameFromServerModel(entityDescriptor); } entry.Properties = this.propertyConverter.PopulateProperties(entityDescriptor.Entity, serverTypeName, entityType.PropertiesToSerialize()); entryWriter.WriteStart(entry, entityDescriptor.Entity); if (EntityStates.Added == entityDescriptor.State) { this.WriteNavigationLink(entityDescriptor, relatedLinks, entryWriter); } entryWriter.WriteEnd(entry, entityDescriptor.Entity); } }
/// <summary> /// Creates and returns an ODataEntry from the given value. /// </summary> /// <param name="entityType">The value type.</param> /// <param name="value">The entry value.</param> /// <param name="properties">The given properties to serialize.</param> /// <returns>An ODataEntry representing the given value.</returns> internal ODataEntry CreateODataEntry(Type entityType, object value, params ClientPropertyAnnotation[] properties) { Debug.Assert(entityType != null, "entityType != null"); Debug.Assert(value != null, "value != null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation entityTypeAnnotation = model.GetClientTypeAnnotation(entityType); Debug.Assert(entityTypeAnnotation != null, "entityTypeAnnotation != null"); Debug.Assert(entityTypeAnnotation.IsEntityType, "Unexpected type"); ODataEntry odataEntityValue = new ODataEntry() { TypeName = entityTypeAnnotation.ElementTypeName, }; string serverTypeName = this.requestInfo.GetServerTypeName(entityTypeAnnotation); odataEntityValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = serverTypeName }); odataEntityValue.Properties = this.PopulateProperties(value, serverTypeName, properties.Any() ? properties : entityTypeAnnotation.PropertiesToSerialize(), null); return(odataEntityValue); }