public void Init() { this._address = new Address { Street = "123 fake st" }; this._entity = new Customer { Id = 1, Address = this._address, Emails = new List<string> { "*****@*****.**"} }; var model = new ClientEdmModel(ODataProtocolVersion.V4); var entityType = model.GetOrCreateEdmType(typeof(Customer)); var complexType = model.GetOrCreateEdmType(typeof(Address)); this._complexValue = new ClientEdmStructuredValue(this._address, model, model.GetClientTypeAnnotation(complexType)); this._entityValue = new ClientEdmStructuredValue(this._entity, model, model.GetClientTypeAnnotation(entityType)); }
private static void VerifyIsVersion4(Type type) { ClientEdmModel model = new ClientEdmModel(ODataProtocolVersion.V4); IEdmType edmType = model.GetOrCreateEdmType(type); var typeAnnotation = model.GetClientTypeAnnotation(edmType); Assert.AreEqual(typeAnnotation.GetMetadataVersion(), new Version(4, 0), "Type with geography point must have 4.0 version"); }
/// <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> /// Converts CLR value into ODataEnumValue. /// </summary> /// <param name="enumClrType">The CLR type.</param> /// <param name="value">The Enum value.</param> /// <param name="isCollectionItem">The bool isCollectionItem.</param> /// <returns>An ODataEnumValue instance.</returns> internal ODataEnumValue CreateODataEnumValue(Type enumClrType, object value, bool isCollectionItem) { Debug.Assert(enumClrType != null && enumClrType.IsEnum, "enumClrType != null && enumClrType.IsEnum"); Debug.Assert(value != null || !isCollectionItem, "Collection items must not be null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation enumTypeAnnotation = model.GetClientTypeAnnotation(enumClrType); Debug.Assert(enumTypeAnnotation != null, "enumTypeAnnotation != null"); Debug.Assert(!enumTypeAnnotation.IsEntityType, "Unexpected entity"); // Handle null value by putting m:null="true" if (value == null) { Debug.Assert(!isCollectionItem, "Null collection items are not supported. Should have already been checked."); return(null); } MemberInfo member = enumClrType.GetMember(value.ToString()).FirstOrDefault(); if (member == null) { throw new NotSupportedException(Strings.Serializer_InvalidEnumMemberValue(enumClrType.Name, value.ToString())); } string memberValue = ClientTypeUtil.GetServerDefinedName(member); return(new ODataEnumValue(memberValue, enumTypeAnnotation.ElementTypeName)); }
/// <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); }
/// <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> /// ConstantExpression visit method /// </summary> /// <param name="c">The ConstantExpression expression to visit</param> /// <returns>The visited ConstantExpression expression </returns> internal override Expression VisitConstant(ConstantExpression c) { if (c.Value == null) { this.builder.Append(UriHelper.NULL); return(c); } // DEVNOTE: // Rather than forcing every other codepath to have the 'Try...' pattern for formatting, // we catch the InvalidOperationException here to change the exception type. // This is exceedingly rare, and not a scenario where performance is meaningful, so the // reduced complexity in all other call sites is worth the extra logic here. string result; BinaryExpression b = this.parent as BinaryExpression; MethodCallExpression m = this.parent as MethodCallExpression; if ((b != null && HasEnumInBinaryExpression(b)) || (m != null && m.Method.Name == "HasFlag")) { c = this.ConvertConstantExpressionForEnum(c); ClientEdmModel model = this.context.Model; IEdmType edmType = model.GetOrCreateEdmType(c.Type.IsEnum() ? c.Type : c.Type.GetGenericArguments()[0]); ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); string typeNameInEdm = this.context.ResolveNameFromTypeInternal(typeAnnotation.ElementType); MemberInfo member = typeAnnotation.ElementType.GetField(c.Value.ToString()); string memberValue = ClientTypeUtil.GetServerDefinedName(member); ODataEnumValue enumValue = new ODataEnumValue(memberValue, typeNameInEdm ?? typeAnnotation.ElementTypeName); result = ODataUriUtils.ConvertToUriLiteral(enumValue, CommonUtil.ConvertToODataVersion(this.uriVersion), null); } else { try { result = LiteralFormatter.ForConstants.Format(c.Value); } catch (InvalidOperationException) { if (this.cantTranslateExpression) { // there's already a problem in the parents. // we should just return here, because caller somewhere up the stack will throw a better exception return(c); } throw new NotSupportedException(Strings.ALinq_CouldNotConvert(c.Value)); } } Debug.Assert(result != null, "result != null"); this.builder.Append(result); return(c); }
/// <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; } }
/// <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> /// Converts CLR value into ODataEnumValue. /// </summary> /// <param name="enumClrType">The CLR type.</param> /// <param name="value">The Enum value.</param> /// <param name="isCollectionItem">The bool isCollectionItem.</param> /// <returns>An ODataEnumValue instance.</returns> internal ODataEnumValue CreateODataEnumValue(Type enumClrType, object value, bool isCollectionItem) { Debug.Assert(enumClrType != null && enumClrType.IsEnum(), "enumClrType != null && enumClrType.IsEnum"); Debug.Assert(value != null || !isCollectionItem, "Collection items must not be null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation enumTypeAnnotation = model.GetClientTypeAnnotation(enumClrType); Debug.Assert(enumTypeAnnotation != null, "enumTypeAnnotation != null"); Debug.Assert(!enumTypeAnnotation.IsEntityType, "Unexpected entity"); // Handle null value by putting m:null="true" if (value == null) { Debug.Assert(!isCollectionItem, "Null collection items are not supported. Should have already been checked."); return(null); } return(new ODataEnumValue(ClientTypeUtil.GetEnumValuesString(value.ToString(), enumClrType), enumTypeAnnotation.ElementTypeName)); }
/// <summary> /// Writes the body operation parameters associated with a ServiceAction. For each BodyOperationParameter: /// 1. calls ODataPropertyConverter to convert CLR object into ODataValue/primitive values. /// 2. then calls ODataParameterWriter to write the ODataValue/primitive values. /// </summary> /// <param name="operationParameters">The list of operation parameters to write.</param> /// <param name="requestMessage">The OData request message used to write the operation parameters.</param> internal void WriteBodyOperationParameters(List <BodyOperationParameter> operationParameters, ODataRequestMessageWrapper requestMessage) { Debug.Assert(requestMessage != null, "requestMessage != null"); Debug.Assert(operationParameters != null, "operationParameters != null"); Debug.Assert(operationParameters.Any(), "operationParameters.Any()"); using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, true /*isParameterPayload*/)) { ODataParameterWriter parameterWriter = messageWriter.CreateODataParameterWriter(null); parameterWriter.WriteStart(); foreach (BodyOperationParameter operationParameter in operationParameters) { if (operationParameter.Value == null) { parameterWriter.WriteValue(operationParameter.Name, operationParameter.Value); } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(operationParameter.Value.GetType()); Debug.Assert(edmType != null, "edmType != null"); switch (edmType.TypeKind) { case EdmTypeKind.Collection: { this.WriteCollectionValueInBodyOperationParameter(parameterWriter, operationParameter, (IEdmCollectionType)edmType); break; } case EdmTypeKind.Complex: case EdmTypeKind.Entity: { Debug.Assert(model.GetClientTypeAnnotation(edmType).ElementType != null, "model.GetClientTypeAnnotation(edmType).ElementType != null"); ODataResourceWrapper entry = this.CreateODataResourceFromEntityOperationParameter(model.GetClientTypeAnnotation(edmType), operationParameter.Value); Debug.Assert(entry != null, "entry != null"); var entryWriter = parameterWriter.CreateResourceWriter(operationParameter.Name); ODataWriterHelper.WriteResource(entryWriter, entry); break; } case EdmTypeKind.Primitive: object primitiveValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(operationParameter.Value, operationParameter.Value.GetType()); parameterWriter.WriteValue(operationParameter.Name, primitiveValue); break; case EdmTypeKind.Enum: ODataEnumValue tmp = this.propertyConverter.CreateODataEnumValue( model.GetClientTypeAnnotation(edmType).ElementType, operationParameter.Value, false); parameterWriter.WriteValue(operationParameter.Name, tmp); break; default: // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(operationParameter.Name, edmType.TypeKind)); } } // else } // foreach parameterWriter.WriteEnd(); parameterWriter.Flush(); } }
/// <summary>Obtain binding info corresponding to a given type</summary> /// <param name="entityType">Type for which to obtain information</param> /// <param name="model">The client model.</param> /// <returns>Info about the <paramref name="entityType"/></returns> private static BindingEntityInfoPerType GetBindingEntityInfoFor(Type entityType, ClientEdmModel model) { BindingEntityInfoPerType bindingEntityInfo; metadataCacheLock.EnterReadLock(); try { if (bindingEntityInfos.TryGetValue(entityType, out bindingEntityInfo)) { return(bindingEntityInfo); } } finally { metadataCacheLock.ExitReadLock(); } bindingEntityInfo = new BindingEntityInfoPerType(); // Try to get the entity set name from the EntitySetAttribute attributes. In order to make the // inheritance work, we need to look at the attributes declared in the base types also. // EntitySetAttribute does not allow multiples, so there can be at most 1 instance on the type. EntitySetAttribute entitySetAttribute = (EntitySetAttribute)entityType.GetCustomAttributes(typeof(EntitySetAttribute), true).SingleOrDefault(); // There must be exactly one (unambiguous) EntitySetAttribute attribute. bindingEntityInfo.EntitySet = entitySetAttribute != null ? entitySetAttribute.EntitySet : null; bindingEntityInfo.ClientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityType)); foreach (ClientPropertyAnnotation p in bindingEntityInfo.ClientType.Properties()) { BindingPropertyInfo bpi = null; Type propertyType = p.PropertyType; if (p.IsStreamLinkProperty) { // DataServiceStreamLink is not mutable externally // It implements INPC to notify controls about our updates // We should ignore its events since we are the only one updating it. continue; } else if (p.IsPrimitiveOrEnumOrComplexCollection) { Debug.Assert(!BindingEntityInfo.IsDataServiceCollection(propertyType, model), "DataServiceCollection cannot be the type that backs collections of primitives or complex types."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection }; } else if (p.IsEntityCollection) { if (BindingEntityInfo.IsDataServiceCollection(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindDataServiceCollection }; } } else if (BindingEntityInfo.IsEntityType(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindEntity }; } else if (BindingEntityInfo.CanBeComplexType(propertyType)) { // Add complex types and nothing else. Debug.Assert(!p.IsKnownType, "Known types do not implement INotifyPropertyChanged."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindComplex }; } if (bpi != null) { bpi.PropertyInfo = p; // For entity types, all of the above types of properties are interesting. // For complex types, only observe collections and complex type properties. if (bindingEntityInfo.ClientType.IsEntityType || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindComplex || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection) { bindingEntityInfo.ObservableProperties.Add(bpi); } } } metadataCacheLock.EnterWriteLock(); try { if (!bindingEntityInfos.ContainsKey(entityType)) { bindingEntityInfos[entityType] = bindingEntityInfo; } } finally { metadataCacheLock.ExitWriteLock(); } return(bindingEntityInfo); }
/// <summary>Obtain binding info corresponding to a given type</summary> /// <param name="entityType">Type for which to obtain information</param> /// <param name="model">The client model.</param> /// <returns>Info about the <paramref name="entityType"/></returns> private static BindingEntityInfoPerType GetBindingEntityInfoFor(Type entityType, ClientEdmModel model) { BindingEntityInfoPerType bindingEntityInfo; metadataCacheLock.EnterReadLock(); try { if (bindingEntityInfos.TryGetValue(entityType, out bindingEntityInfo)) { return bindingEntityInfo; } } finally { metadataCacheLock.ExitReadLock(); } bindingEntityInfo = new BindingEntityInfoPerType(); // Try to get the entity set name from the EntitySetAttribute attributes. In order to make the // inheritance work, we need to look at the attributes declared in the base types also. // EntitySetAttribute does not allow multiples, so there can be at most 1 instance on the type. EntitySetAttribute entitySetAttribute = (EntitySetAttribute)entityType.GetCustomAttributes(typeof(EntitySetAttribute), true).SingleOrDefault(); // There must be exactly one (unambiguous) EntitySetAttribute attribute. bindingEntityInfo.EntitySet = entitySetAttribute != null ? entitySetAttribute.EntitySet : null; bindingEntityInfo.ClientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityType)); foreach (ClientPropertyAnnotation p in bindingEntityInfo.ClientType.Properties()) { BindingPropertyInfo bpi = null; Type propertyType = p.PropertyType; if (p.IsStreamLinkProperty) { // DataServiceStreamLink is not mutable externally // It implements INPC to notify controls about our updates // We should ignore its events since we are the only one updating it. continue; } else if (p.IsPrimitiveOrEnumOrComplexCollection) { Debug.Assert(!BindingEntityInfo.IsDataServiceCollection(propertyType, model), "DataServiceCollection cannot be the type that backs collections of primitives or complex types."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection }; } else if (p.IsEntityCollection) { if (BindingEntityInfo.IsDataServiceCollection(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindDataServiceCollection }; } } else if (BindingEntityInfo.IsEntityType(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindEntity }; } else if (BindingEntityInfo.CanBeComplexType(propertyType)) { // Add complex types and nothing else. Debug.Assert(!p.IsKnownType, "Known types do not implement INotifyPropertyChanged."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindComplex }; } if (bpi != null) { bpi.PropertyInfo = p; // For entity types, all of the above types of properties are interesting. // For complex types, only observe collections and complex type properties. if (bindingEntityInfo.ClientType.IsEntityType || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindComplex || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection) { bindingEntityInfo.ObservableProperties.Add(bpi); } } } metadataCacheLock.EnterWriteLock(); try { if (!bindingEntityInfos.ContainsKey(entityType)) { bindingEntityInfos[entityType] = bindingEntityInfo; } } finally { metadataCacheLock.ExitWriteLock(); } return bindingEntityInfo; }
/// <summary> /// Writes the body operation parameters associated with a ServiceAction. For each BodyOperationParameter: /// 1. calls ODataPropertyConverter to convert CLR object into ODataValue/primitive values. /// 2. then calls ODataParameterWriter to write the ODataValue/primitive values. /// </summary> /// <param name="operationParameters">The list of operation parameters to write.</param> /// <param name="requestMessage">The OData request message used to write the operation parameters.</param> internal void WriteBodyOperationParameters(List <BodyOperationParameter> operationParameters, ODataRequestMessageWrapper requestMessage) { Debug.Assert(requestMessage != null, "requestMessage != null"); Debug.Assert(operationParameters != null, "operationParameters != null"); Debug.Assert(operationParameters.Any(), "operationParameters.Any()"); using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, true /*isParameterPayload*/)) { ODataParameterWriter parameterWriter = messageWriter.CreateODataParameterWriter((IEdmOperation)null); parameterWriter.WriteStart(); foreach (OperationParameter operationParameter in operationParameters) { if (operationParameter.Value == null) { parameterWriter.WriteValue(operationParameter.Name, operationParameter.Value); } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(operationParameter.Value.GetType()); Debug.Assert(edmType != null, "edmType != null"); switch (edmType.TypeKind) { case EdmTypeKind.Collection: { // TODO: just call ODataPropertyConverter.CreateODataCollection() IEnumerator enumerator = ((ICollection)operationParameter.Value).GetEnumerator(); ODataCollectionWriter collectionWriter = parameterWriter.CreateCollectionWriter(operationParameter.Name); ODataCollectionStart odataCollectionStart = new ODataCollectionStart(); collectionWriter.WriteStart(odataCollectionStart); while (enumerator.MoveNext()) { Object collectionItem = enumerator.Current; if (collectionItem == null) { throw new NotSupportedException(Strings.Serializer_NullCollectionParamterItemValue(operationParameter.Name)); } IEdmType edmItemType = model.GetOrCreateEdmType(collectionItem.GetType()); Debug.Assert(edmItemType != null, "edmItemType != null"); switch (edmItemType.TypeKind) { case EdmTypeKind.Complex: { Debug.Assert(model.GetClientTypeAnnotation(edmItemType).ElementType != null, "edmItemType.GetClientTypeAnnotation().ElementType != null"); ODataComplexValue complexValue = this.propertyConverter.CreateODataComplexValue( model.GetClientTypeAnnotation(edmItemType).ElementType, collectionItem, null /*propertyName*/, false /*isCollectionItem*/, null /*visitedComplexTypeObjects*/); Debug.Assert(complexValue != null, "complexValue != null"); collectionWriter.WriteItem(complexValue); break; } case EdmTypeKind.Primitive: { object primitiveItemValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(collectionItem, collectionItem.GetType()); collectionWriter.WriteItem(primitiveItemValue); break; } case EdmTypeKind.Enum: { ODataEnumValue enumTmp = this.propertyConverter.CreateODataEnumValue( model.GetClientTypeAnnotation(edmItemType).ElementType, collectionItem, false); collectionWriter.WriteItem(enumTmp); break; } default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(operationParameter.Name, edmItemType.TypeKind)); } } collectionWriter.WriteEnd(); collectionWriter.Flush(); break; } case EdmTypeKind.Complex: { Debug.Assert(model.GetClientTypeAnnotation(edmType).ElementType != null, "model.GetClientTypeAnnotation(edmType).ElementType != null"); ODataComplexValue complexValue = this.propertyConverter.CreateODataComplexValue( model.GetClientTypeAnnotation(edmType).ElementType, operationParameter.Value, null /*propertyName*/, false /*isCollectionItemType*/, null /*visitedComplexTypeObjects*/); Debug.Assert(complexValue != null, "complexValue != null"); parameterWriter.WriteValue(operationParameter.Name, complexValue); break; } case EdmTypeKind.Primitive: object primitiveValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(operationParameter.Value, operationParameter.Value.GetType()); parameterWriter.WriteValue(operationParameter.Name, primitiveValue); break; case EdmTypeKind.Enum: ODataEnumValue tmp = this.propertyConverter.CreateODataEnumValue( model.GetClientTypeAnnotation(edmType).ElementType, operationParameter.Value, false); parameterWriter.WriteValue(operationParameter.Name, tmp); break; default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(operationParameter.Name, edmType.TypeKind)); } } // else } // foreach parameterWriter.WriteEnd(); parameterWriter.Flush(); } }
private string ConvertToEscapedUriValue(string paramName, object value) { Debug.Assert(!string.IsNullOrEmpty(paramName), "!string.IsNullOrEmpty(paramName)"); Object valueInODataFormat = null; // Literal values with single quotes need special escaping due to System.Uri changes in behavior between .NET 4.0 and 4.5. // We need to ensure that our escaped values do not change between those versions, so we need to escape values differently when they could contain single quotes. bool needsSpecialEscaping = false; if (value == null) { needsSpecialEscaping = true; } else { if (value is ODataNullValue) { valueInODataFormat = value; needsSpecialEscaping = true; } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(value.GetType()); Debug.Assert(edmType != null, "edmType != null"); switch (edmType.TypeKind) { case EdmTypeKind.Primitive: valueInODataFormat = value; needsSpecialEscaping = true; break; case EdmTypeKind.Enum: { ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); string typeNameInEdm = this.requestInfo.GetServerTypeName(model.GetClientTypeAnnotation(edmType)); MemberInfo member = typeAnnotation.ElementType.GetMember(value.ToString()).FirstOrDefault(); if (member == null) { throw new NotSupportedException(Strings.Serializer_InvalidEnumMemberValue(typeAnnotation.ElementType.Name, value.ToString())); } string memberValue = ClientTypeUtil.GetServerDefinedName(member); valueInODataFormat = new ODataEnumValue(memberValue, typeNameInEdm ?? typeAnnotation.ElementTypeName); needsSpecialEscaping = true; } break; case EdmTypeKind.Complex: { ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = this.propertyConverter.CreateODataComplexValue(typeAnnotation.ElementType, value, null /*propertyName*/, false /*isCollectionItemType*/, null /*visitedComplexTypeObjects*/); // When using JsonVerbose to format query string parameters for Actions, // we cannot write out Complex values in the URI without the type name of the complex type in the JSON payload. // If this value is null, the client has to set the ResolveName property on the DataServiceContext instance. ODataComplexValue complexValue = (ODataComplexValue)valueInODataFormat; SerializationTypeNameAnnotation serializedTypeNameAnnotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializedTypeNameAnnotation == null || string.IsNullOrEmpty(serializedTypeNameAnnotation.TypeName)) { throw Error.InvalidOperation(Strings.DataServiceException_GeneralError); } } break; case EdmTypeKind.Collection: IEdmCollectionType edmCollectionType = edmType as IEdmCollectionType; Debug.Assert(edmCollectionType != null, "edmCollectionType != null"); IEdmTypeReference itemTypeReference = edmCollectionType.ElementType; Debug.Assert(itemTypeReference != null, "itemTypeReference != null"); ClientTypeAnnotation itemTypeAnnotation = model.GetClientTypeAnnotation(itemTypeReference.Definition); Debug.Assert(itemTypeAnnotation != null, "itemTypeAnnotation != null"); switch (itemTypeAnnotation.EdmType.TypeKind) { // We only support primitive, Enum or complex type as a collection item type. case EdmTypeKind.Primitive: case EdmTypeKind.Enum: case EdmTypeKind.Complex: break; default: throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(paramName, itemTypeAnnotation.EdmType.TypeKind)); } valueInODataFormat = this.propertyConverter.CreateODataCollection( itemTypeAnnotation.ElementType, null /*propertyName*/, value, null /*visitedComplexTypeObjects*/); break; default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } // When calling Execute() to invoke an Action, the client doesn't support parsing the target url // to determine which IEdmOperationImport to pass to the ODL writer. So the ODL writer is // serializing the parameter payload without metadata. Setting the model to null so ODL doesn't // do unecessary validations when writing without metadata. string literal = ODataUriUtils.ConvertToUriLiteral(valueInODataFormat, CommonUtil.ConvertToODataVersion(this.requestInfo.MaxProtocolVersionAsVersion), null /* edmModel */); // The value from ConvertToUriValue will not be escaped, but will already contain literal delimiters like single quotes, so we // need to use our own escape method that will preserve those characters instead of directly calling Uri.EscapeDataString that may escape them. // This is only necessary for primitives and nulls because the other structures are serialized using the JSON format and it uses double quotes // which have always been escaped. if (needsSpecialEscaping) { return(DataStringEscapeBuilder.EscapeDataString(literal)); } return(Uri.EscapeDataString(literal)); }
/// <summary> /// Constructor /// </summary> /// <param name="source">Source entity</param> /// <param name="sourceProperty">Navigation property on the source entity</param> /// <param name="target">Target entity</param> /// <param name="model">The client model.</param> internal LinkDescriptor(object source, string sourceProperty, object target, ClientEdmModel model) : this(source, sourceProperty, target, EntityStates.Unchanged) { this.IsSourcePropertyCollection = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(source.GetType())).GetProperty(sourceProperty, false).IsEntityCollection; }
/// <summary> /// Converts the object to ODataValue, the result could be null, the original primitive object, ODataNullValue, /// ODataEnumValue, ODataCollectionValue, ODataResource, ODataEntityReferenceLinks, ODataEntityReferenceLinks, or /// a list of ODataResource. /// </summary> /// <param name="paramName">The name of the <see cref="UriOperationParameter"/>. Used for error reporting.</param> /// <param name="value">The value of the <see cref="UriOperationParameter"/>.</param> /// <param name="needsSpecialEscaping">True if the result need special escaping.</param> /// <param name="useEntityReference">If true, use entity reference, instead of entity to serialize the parameter.</param> /// <returns>The converted result.</returns> private object ConvertToODataValue(string paramName, object value, ref bool needsSpecialEscaping, bool useEntityReference) { Object valueInODataFormat = null; if (value == null) { needsSpecialEscaping = true; } else if (value is ODataNullValue) { valueInODataFormat = value; needsSpecialEscaping = true; } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(value.GetType()); Debug.Assert(edmType != null, "edmType != null"); ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); switch (edmType.TypeKind) { case EdmTypeKind.Primitive: // Client lib internal conversion to support DateTime if (value is DateTime) { valueInODataFormat = PlatformHelper.ConvertDateTimeToDateTimeOffset((DateTime)value); } else { valueInODataFormat = value; } needsSpecialEscaping = true; break; case EdmTypeKind.Enum: string typeNameInEdm = this.requestInfo.GetServerTypeName(model.GetClientTypeAnnotation(edmType)); valueInODataFormat = new ODataEnumValue( ClientTypeUtil.GetEnumValuesString(value.ToString(), typeAnnotation.ElementType), typeNameInEdm ?? typeAnnotation.ElementTypeName); needsSpecialEscaping = true; break; case EdmTypeKind.Collection: IEdmCollectionType edmCollectionType = edmType as IEdmCollectionType; Debug.Assert(edmCollectionType != null, "edmCollectionType != null"); IEdmTypeReference itemTypeReference = edmCollectionType.ElementType; Debug.Assert(itemTypeReference != null, "itemTypeReference != null"); ClientTypeAnnotation itemTypeAnnotation = model.GetClientTypeAnnotation(itemTypeReference.Definition); Debug.Assert(itemTypeAnnotation != null, "itemTypeAnnotation != null"); valueInODataFormat = ConvertToCollectionValue(paramName, value, itemTypeAnnotation, useEntityReference); break; case EdmTypeKind.Complex: case EdmTypeKind.Entity: Debug.Assert(edmType.TypeKind == EdmTypeKind.Complex || value != null, "edmType.TypeKind == EdmTypeKind.Complex || value != null"); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = ConvertToEntityValue(value, typeAnnotation.ElementType, useEntityReference); break; default: // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } return(valueInODataFormat); }
/// <summary> /// Writes collection value in body operation parameter. /// </summary> /// <param name="parameterWriter">The odata parameter writer.</param> /// <param name="operationParameter">The operation parameter.</param> /// <param name="edmCollectionType">The edm collection type.</param> private void WriteCollectionValueInBodyOperationParameter(ODataParameterWriter parameterWriter, BodyOperationParameter operationParameter, IEdmCollectionType edmCollectionType) { ClientEdmModel model = this.requestInfo.Model; var elementTypeKind = edmCollectionType.ElementType.TypeKind(); if (elementTypeKind == EdmTypeKind.Entity || elementTypeKind == EdmTypeKind.Complex) { ODataWriter feedWriter = parameterWriter.CreateResourceSetWriter(operationParameter.Name); feedWriter.WriteStart(new ODataResourceSet()); IEnumerator enumerator = ((ICollection)operationParameter.Value).GetEnumerator(); while (enumerator.MoveNext()) { Object collectionItem = enumerator.Current; if (collectionItem == null) { if (elementTypeKind == EdmTypeKind.Complex) { feedWriter.WriteStart((ODataResource)null); feedWriter.WriteEnd(); continue; } else { throw new NotSupportedException(Strings.Serializer_NullCollectionParamterItemValue(operationParameter.Name)); } } IEdmType edmItemType = model.GetOrCreateEdmType(collectionItem.GetType()); Debug.Assert(edmItemType != null, "edmItemType != null"); if (edmItemType.TypeKind != EdmTypeKind.Entity && edmItemType.TypeKind != EdmTypeKind.Complex) { throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(operationParameter.Name, edmItemType.TypeKind)); } Debug.Assert(model.GetClientTypeAnnotation(edmItemType).ElementType != null, "edmItemType.GetClientTypeAnnotation().ElementType != null"); ODataResourceWrapper entry = this.CreateODataResourceFromEntityOperationParameter(model.GetClientTypeAnnotation(edmItemType), collectionItem); Debug.Assert(entry != null, "entry != null"); ODataWriterHelper.WriteResource(feedWriter, entry); } feedWriter.WriteEnd(); feedWriter.Flush(); } else { ODataCollectionWriter collectionWriter = parameterWriter.CreateCollectionWriter(operationParameter.Name); ODataCollectionStart odataCollectionStart = new ODataCollectionStart(); collectionWriter.WriteStart(odataCollectionStart); IEnumerator enumerator = ((ICollection)operationParameter.Value).GetEnumerator(); while (enumerator.MoveNext()) { Object collectionItem = enumerator.Current; if (collectionItem == null) { collectionWriter.WriteItem(null); continue; } IEdmType edmItemType = model.GetOrCreateEdmType(collectionItem.GetType()); Debug.Assert(edmItemType != null, "edmItemType != null"); switch (edmItemType.TypeKind) { case EdmTypeKind.Primitive: { object primitiveItemValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(collectionItem, collectionItem.GetType()); collectionWriter.WriteItem(primitiveItemValue); break; } case EdmTypeKind.Enum: { ODataEnumValue enumTmp = this.propertyConverter.CreateODataEnumValue(model.GetClientTypeAnnotation(edmItemType).ElementType, collectionItem, false); collectionWriter.WriteItem(enumTmp); break; } default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(operationParameter.Name, edmItemType.TypeKind)); } } collectionWriter.WriteEnd(); collectionWriter.Flush(); } }
/// <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> /// Converts the object to ODataValue, the result could be null, the original primitive object, ODataNullValue, /// ODataEnumValue, ODataCollectionValue, ODataEntry, ODataEntityReferenceLinks, ODataEntityReferenceLinks, or /// a list of ODataEntry. /// </summary> /// <param name="paramName">The name of the <see cref="UriOperationParameter"/>. Used for error reporting.</param> /// <param name="value">The value of the <see cref="UriOperationParameter"/>.</param> /// <param name="needsSpecialEscaping">True if the result need special escaping.</param> /// <param name="useEntityReference">If true, use entity reference, instead of entity to serialize the parameter.</param> /// <returns>The converted result.</returns> private object ConvertToODataValue(string paramName, object value, ref bool needsSpecialEscaping, bool useEntityReference) { Object valueInODataFormat = null; if (value == null) { needsSpecialEscaping = true; } else if (value is ODataNullValue) { valueInODataFormat = value; needsSpecialEscaping = true; } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(value.GetType()); Debug.Assert(edmType != null, "edmType != null"); ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); switch (edmType.TypeKind) { case EdmTypeKind.Primitive: valueInODataFormat = value; needsSpecialEscaping = true; break; case EdmTypeKind.Enum: string typeNameInEdm = this.requestInfo.GetServerTypeName(model.GetClientTypeAnnotation(edmType)); valueInODataFormat = new ODataEnumValue( ClientTypeUtil.GetEnumValuesString(value.ToString(), typeAnnotation.ElementType), typeNameInEdm ?? typeAnnotation.ElementTypeName); needsSpecialEscaping = true; break; case EdmTypeKind.Complex: Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = this.propertyConverter.CreateODataComplexValue(typeAnnotation.ElementType, value, null, false, null); // When using JsonVerbose to format query string parameters for Actions, // we cannot write out Complex values in the URI without the type name of the complex type in the JSON payload. // If this value is null, the client has to set the ResolveName property on the DataServiceContext instance. ODataComplexValue complexValue = (ODataComplexValue)valueInODataFormat; SerializationTypeNameAnnotation serializedTypeNameAnnotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializedTypeNameAnnotation == null || string.IsNullOrEmpty(serializedTypeNameAnnotation.TypeName)) { throw Error.InvalidOperation(Strings.DataServiceException_GeneralError); } break; case EdmTypeKind.Collection: IEdmCollectionType edmCollectionType = edmType as IEdmCollectionType; Debug.Assert(edmCollectionType != null, "edmCollectionType != null"); IEdmTypeReference itemTypeReference = edmCollectionType.ElementType; Debug.Assert(itemTypeReference != null, "itemTypeReference != null"); ClientTypeAnnotation itemTypeAnnotation = model.GetClientTypeAnnotation(itemTypeReference.Definition); Debug.Assert(itemTypeAnnotation != null, "itemTypeAnnotation != null"); valueInODataFormat = ConvertToCollectionValue(paramName, value, itemTypeAnnotation, useEntityReference); break; case EdmTypeKind.Entity: Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = ConvertToEntityValue(value, typeAnnotation.ElementType, useEntityReference); break; default: // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } return(valueInODataFormat); }
/// <summary> /// Check to see if the resource to be inserted is a media descriptor, and if so /// setup a POST request for the media content first and turn the rest of /// the operation into a PUT to update the rest of the properties. /// </summary> /// <param name="entityDescriptor">The resource to check/process</param> /// <returns>An instance of ODataRequestMessage to do POST to the media resource</returns> private ODataRequestMessageWrapper CheckAndProcessMediaEntryPost(EntityDescriptor entityDescriptor) { // TODO: Revisit the design of how media link entries are handled during update ClientEdmModel model = this.RequestInfo.Model; ClientTypeAnnotation type = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType())); if (!type.IsMediaLinkEntry && !entityDescriptor.IsMediaLinkEntry) { // this is not a media link descriptor, process normally return(null); } if (type.MediaDataMember == null && entityDescriptor.SaveStream == null) { // The entity is marked as MLE but we don't have the content property // and the user didn't set the save stream. throw Error.InvalidOperation(Strings.Context_MLEWithoutSaveStream(type.ElementTypeName)); } Debug.Assert( (type.MediaDataMember != null && entityDescriptor.SaveStream == null) || (type.MediaDataMember == null && entityDescriptor.SaveStream != null), "Only one way of specifying the MR content is allowed."); ODataRequestMessageWrapper mediaRequest = null; if (type.MediaDataMember != null) { string contentType = null; int contentLength = 0; if (type.MediaDataMember.MimeTypeProperty == null) { contentType = XmlConstants.MimeApplicationOctetStream; } else { object mimeTypeValue = type.MediaDataMember.MimeTypeProperty.GetValue(entityDescriptor.Entity); String mimeType = mimeTypeValue != null?mimeTypeValue.ToString() : null; if (String.IsNullOrEmpty(mimeType)) { throw Error.InvalidOperation( Strings.Context_NoContentTypeForMediaLink( type.ElementTypeName, type.MediaDataMember.MimeTypeProperty.PropertyName)); } contentType = mimeType; } object value = type.MediaDataMember.GetValue(entityDescriptor.Entity); if (value == null) { this.mediaResourceRequestStream = null; } else { byte[] buffer = value as byte[]; if (buffer == null) { string mime; Encoding encoding; ContentTypeUtil.ReadContentType(contentType, out mime, out encoding); if (encoding == null) { encoding = Encoding.UTF8; contentType += XmlConstants.MimeTypeUtf8Encoding; } buffer = encoding.GetBytes(ClientConvert.ToString(value)); } contentLength = buffer.Length; // Need to specify that the buffer is publicly visible as we need to access it later on this.mediaResourceRequestStream = new MemoryStream(buffer, 0, buffer.Length, false, true); } HeaderCollection headers = new HeaderCollection(); headers.SetHeader(XmlConstants.HttpContentLength, contentLength.ToString(CultureInfo.InvariantCulture)); headers.SetHeader(XmlConstants.HttpContentType, contentType); mediaRequest = this.CreateMediaResourceRequest( entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/), XmlConstants.HttpMethodPost, Util.ODataVersion4, type.MediaDataMember == null, // sendChunked true, // applyResponsePreference headers, entityDescriptor); } else { HeaderCollection headers = new HeaderCollection(); this.SetupMediaResourceRequest(headers, entityDescriptor.SaveStream, null /*etag*/); mediaRequest = this.CreateMediaResourceRequest( entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/), XmlConstants.HttpMethodPost, Util.ODataVersion4, type.MediaDataMember == null, // sendChunked true, // applyResponsePreference headers, entityDescriptor); } // Convert the insert into an update for the media link descriptor we just created // (note that the identity still needs to be fixed up on the resbox once // the response comes with the 'location' header; that happens during processing // of the response in SavedResource()) entityDescriptor.State = EntityStates.Modified; return(mediaRequest); }