/// <summary> /// User hook to resolve name into a type. /// </summary> /// <param name="wireName">Name to resolve.</param> /// <param name="expectedType">Expected type for the name.</param> /// <returns>the type as returned by the resolver. If no resolver is registered or resolver returns null, expected type is returned.</returns> /// <exception cref="InvalidOperationException">if ResolveType function returns a type not assignable to the userType</exception> private Type ResolveTypeFromName(string wireName, Type expectedType) { Debug.Assert(!String.IsNullOrEmpty(wireName), "!String.IsNullOrEmpty(wireName)"); Debug.Assert(expectedType != null, "userType != null"); // If there is a mismatch between the wireName and expected type (For e.g. wireName is Edm.Int32 and expectedType is a complex type) // we will return Edm.Int32 from this method and ODatalib fails stating the type kind do not match. Type payloadType; if (!ClientConvert.ToNamedType(wireName, out payloadType)) { payloadType = this.resolveTypeFromName(wireName); if (payloadType == null) { // if the type resolution method returns null or the ResolveType property was not set #if !PORTABLELIB payloadType = ClientTypeCache.ResolveFromName(wireName, expectedType); #else payloadType = ClientTypeCache.ResolveFromName(wireName, expectedType, this.GetType()); #endif } if (!this.skipTypeAssignabilityCheck && (payloadType != null) && (!expectedType.IsAssignableFrom(payloadType))) { // throw an exception if the type from the resolver is not assignable to the expected type throw Error.InvalidOperation(Strings.Deserialize_Current(expectedType, payloadType)); } } return(payloadType ?? expectedType); }
private MaterializeAtom ReadPropertyFromRawData(ClientPropertyAnnotation property) { DataServiceContext context = (DataServiceContext)this.Source; bool merging = context.ApplyingChanges; try { context.ApplyingChanges = true; // if this is the data property for a media entry, what comes back // is the raw value (no markup) #if ASTORIA_OPEN_OBJECT object openProps = null; #endif string mimeType = null; Encoding encoding = null; Type elementType = property.EntityCollectionItemType ?? property.NullablePropertyType; IList results = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(elementType)); ContentTypeUtil.ReadContentType(this.ContentType, out mimeType, out encoding); using (Stream responseStream = this.GetResponseStream()) { // special case byte[], and for everything else let std conversion kick-in if (property.PropertyType == typeof(byte[])) { int total = checked ((int)this.ContentLength); byte[] buffer = null; if (total >= 0) { buffer = LoadPropertyResult.ReadByteArrayWithContentLength(responseStream, total); } else { buffer = LoadPropertyResult.ReadByteArrayChunked(responseStream); } results.Add(buffer); #if ASTORIA_OPEN_OBJECT property.SetValue(this.entity, buffer, this.propertyName, ref openProps, false); #else property.SetValue(this.entity, buffer, this.propertyName, false); #endif } else { // responseStream will disposed, StreamReader doesn't need to dispose of it. StreamReader reader = new StreamReader(responseStream, encoding); object convertedValue = property.PropertyType == typeof(string) ? reader.ReadToEnd() : ClientConvert.ChangeType(reader.ReadToEnd(), property.PropertyType); results.Add(convertedValue); #if ASTORIA_OPEN_OBJECT property.SetValue(this.entity, convertedValue, this.propertyName, ref openProps, false); #else property.SetValue(this.entity, convertedValue, this.propertyName, false); #endif } } #if ASTORIA_OPEN_OBJECT Debug.Assert(openProps == null, "These should not be set in this path"); #endif if (property.MimeTypeProperty != null) { // an implication of this 3rd-arg-null is that mime type properties cannot be open props #if ASTORIA_OPEN_OBJECT property.MimeTypeProperty.SetValue(this.entity, mimeType, null, ref openProps, false); Debug.Assert(openProps == null, "These should not be set in this path"); #else property.MimeTypeProperty.SetValue(this.entity, mimeType, null, false); #endif } return(MaterializeAtom.CreateWrapper(context, results)); } finally { context.ApplyingChanges = merging; } }
/// <summary> /// Materialize by calling the Parse method on the converter /// </summary> /// <param name="clrType">clrType</param> /// <returns>A materialized instance</returns> internal override object Materialize(Type clrType) { return(ClientConvert.ChangeType(this.Text, clrType)); }
/// <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); }
public T Field <T>(string property) { return((T)ClientConvert.VerifyCast(typeof(T), this[property])); }
/// <summary> /// Creates and returns an ODataCollectionValue from the given value. /// </summary> /// <param name="collectionItemType">The type of the 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="value">The value.</param> /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param> /// <param name="isDynamicProperty">Whether this collection property is a dynamic property</param> /// <param name="setTypeAnnotation">If true, set the type annotation on ODataValue.</param> /// <returns>An ODataCollectionValue representing the given value.</returns> internal ODataCollectionValue CreateODataCollection(Type collectionItemType, string propertyName, object value, HashSet <object> visitedComplexTypeObjects, bool isDynamicProperty, bool setTypeAnnotation = true) { Debug.Assert(collectionItemType != null, "collectionItemType != null"); WebUtil.ValidateCollection(collectionItemType, value, propertyName, isDynamicProperty); PrimitiveType ptype; bool isCollectionOfPrimitiveTypes = PrimitiveType.TryGetPrimitiveType(collectionItemType, out ptype); ODataCollectionValue collection = new ODataCollectionValue(); IEnumerable enumerablePropertyValue = (IEnumerable)value; string collectionItemTypeName; string collectionTypeName; if (isCollectionOfPrimitiveTypes) { collectionItemTypeName = ClientConvert.GetEdmType(Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType); if (enumerablePropertyValue != null) { collection.Items = Util.GetEnumerable( enumerablePropertyValue, (val) => { if (val == null) { return(null); } WebUtil.ValidatePrimitiveCollectionItem(val, propertyName, collectionItemType); return(ConvertPrimitiveValueToRecognizedODataType(val, collectionItemType)); }); } // TypeName for primitives should be the EDM name since that's what we will be able to look up in the model collectionTypeName = collectionItemTypeName; } else { Type collectionItemTypeTmp = Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType; // Note that the collectionItemTypeName will be null if the context does not have the ResolveName func. collectionItemTypeName = this.requestInfo.ResolveNameFromType(collectionItemType); if (enumerablePropertyValue != null) { collection.Items = Util.GetEnumerable( enumerablePropertyValue, (val) => { if (val == null) { return(new ODataEnumValue(null, collectionItemType.FullName) as ODataValue); } return(new ODataEnumValue(ClientTypeUtil.GetEnumValuesString(val.ToString(), collectionItemTypeTmp), collectionItemType.FullName) as ODataValue); }); } // TypeName for complex types needs to be the client type name (not the one we resolved above) since it will be looked up in the client model collectionTypeName = collectionItemType.FullName; } // Set the type name to use for client type lookups and validation. collection.TypeName = GetCollectionName(collectionTypeName); // Ideally, we should not set type annotation on collection value. // To keep backward compatibility, we'll keep it in request body, but do not include it in url. if (setTypeAnnotation) { string wireTypeName = GetCollectionName(collectionItemTypeName); collection.TypeAnnotation = new ODataTypeAnnotation(wireTypeName); } return(collection); }
/// <summary> /// Creates and returns an ODataCollectionValue from the given value. /// </summary> /// <param name="collectionItemType">The type of the 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="value">The value.</param> /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param> /// <param name="isDynamicProperty">Whether this collection property is a dynamic property</param> /// <returns>An ODataCollectionValue representing the given value.</returns> internal ODataCollectionValue CreateODataCollection(Type collectionItemType, string propertyName, object value, HashSet <object> visitedComplexTypeObjects, bool isDynamicProperty) { Debug.Assert(collectionItemType != null, "collectionItemType != null"); WebUtil.ValidateCollection(collectionItemType, value, propertyName, isDynamicProperty); PrimitiveType ptype; bool isCollectionOfPrimitiveTypes = PrimitiveType.TryGetPrimitiveType(collectionItemType, out ptype); ODataCollectionValue collection = new ODataCollectionValue(); IEnumerable enumerablePropertyValue = (IEnumerable)value; string collectionItemTypeName; string collectionTypeName; if (isCollectionOfPrimitiveTypes) { collectionItemTypeName = ClientConvert.GetEdmType(Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType); if (enumerablePropertyValue != null) { collection.Items = Util.GetEnumerable( enumerablePropertyValue, (val) => { WebUtil.ValidateCollectionItem(val); WebUtil.ValidatePrimitiveCollectionItem(val, propertyName, collectionItemType); return(ConvertPrimitiveValueToRecognizedODataType(val, collectionItemType)); }); } // TypeName for primitives should be the EDM name since that's what we will be able to look up in the model collectionTypeName = collectionItemTypeName; } else { Type collectionItemTypeTmp = Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType; bool areEnumItems = collectionItemTypeTmp.IsEnum(); // Note that the collectionItemTypeName will be null if the context does not have the ResolveName func. collectionItemTypeName = this.requestInfo.ResolveNameFromType(collectionItemType); if (enumerablePropertyValue != null) { collection.Items = Util.GetEnumerable( enumerablePropertyValue, (val) => { if (areEnumItems) { if (val == null) { return(new ODataEnumValue(null, collectionItemType.FullName) as ODataValue); } return(new ODataEnumValue(ClientTypeUtil.GetEnumValuesString(val.ToString(), collectionItemTypeTmp), collectionItemType.FullName) as ODataValue); } else { WebUtil.ValidateCollectionItem(val); WebUtil.ValidateComplexCollectionItem(val, propertyName, collectionItemType); return(this.CreateODataComplexValue(collectionItemType, val, propertyName, true /*isCollectionItem*/, visitedComplexTypeObjects) as ODataValue); } }); } // TypeName for complex types needs to be the client type name (not the one we resolved above) since it will be looked up in the client model collectionTypeName = collectionItemType.FullName; } // Set the type name to use for client type lookups and validation. Because setting this value can cause validation to occur, we will // only do it for JSON Light, in order to avoid breaking changes with the WCF Data Services 5.0 release, since it was already shipped without this. if (!this.requestInfo.Format.UsingAtom) { collection.TypeName = GetCollectionName(collectionTypeName); } string wireTypeName = GetCollectionName(collectionItemTypeName); collection.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = wireTypeName }); return(collection); }