/// <summary> /// Implementation of Read>. /// </summary> /// <returns> /// Return value of Read/> /// </returns> protected sealed override bool ReadImplementation() { if (!this.hasReadValue) { try { ClientEdmModel model = this.MaterializerContext.Model; Type expectedType = this.ExpectedType; IEdmTypeReference expectedClientType = model.GetOrCreateEdmType(expectedType).ToEdmTypeReference(ClientTypeUtil.CanAssignNull(expectedType)); if ((this.SingleResult.HasValue && !this.SingleResult.Value) && expectedClientType.Definition.TypeKind != EdmTypeKind.Collection) { expectedType = typeof(ICollection <>).MakeGenericType(expectedType); // we do not allow null values for collection expectedClientType = model.GetOrCreateEdmType(expectedType).ToEdmTypeReference(false); } IEdmTypeReference expectedReaderType = this.MaterializerContext.ResolveExpectedTypeForReading(expectedType).ToEdmTypeReference(expectedClientType.IsNullable); this.ReadWithExpectedType(expectedClientType, expectedReaderType); } catch (ODataErrorException e) { throw new DataServiceClientException(ClientStrings.Deserialize_ServerException(e.Error.Message), e); } catch (ODataException e) { throw new InvalidOperationException(e.Message, e); } catch (ArgumentException e) { throw new InvalidOperationException(e.Message, e); } finally { this.hasReadValue = true; } return(true); } else { return(false); } }
/// <summary> /// Gets the raw CLR value for the given <see cref="IEdmPropertyValue"/>. /// </summary> /// <param name="property">The property to get the value for.</param> /// <param name="type">The type which declared the property.</param> /// <returns>The raw CLR value of the property.</returns> private static object GetPropertyValue(IEdmPropertyValue property, IEdmTypeReference type) { Debug.Assert(property != null, "property != null"); IEdmValue propertyValue = property.Value; // DEVNOTE: though this check is not strictly necessary, and would be caught by later checks, // it seems worthwhile to fail fast if we can. if (propertyValue.ValueKind == EdmValueKind.Null) { throw Error.InvalidOperation(ErrorStrings.Context_NullKeysAreNotSupported(property.Name)); } var primitiveValue = propertyValue as IEdmPrimitiveValue; if (primitiveValue == null) { throw Error.InvalidOperation(ErrorStrings.ClientType_KeysMustBeSimpleTypes(type.FullName())); } // DEVNOTE: This can return null, and will be handled later. The reason for this is that the client // and server have different ways of getting property values, but both will eventually hit the same // codepath and that is where the null is handled. return(primitiveValue.ToClrValue()); }
public bool AddCollection( object source, string sourceProperty, object collection, string collectionEntitySet) { Debug.Assert(collection != null, "'collection' can not be null"); Debug.Assert( BindingEntityInfo.IsDataServiceCollection(collection.GetType()), "Argument 'collection' must be an DataServiceCollection<T> of entity type T"); if (this.graph.ExistsVertex(collection)) { return(false); } Vertex collectionVertex = this.graph.AddVertex(collection); collectionVertex.IsCollection = true; collectionVertex.EntitySet = collectionEntitySet; ICollection collectionItf = collection as ICollection; if (source != null) { collectionVertex.Parent = this.graph.LookupVertex(source); collectionVertex.ParentProperty = sourceProperty; this.graph.AddEdge(source, collection, sourceProperty); Type entityType = BindingUtils.GetCollectionEntityType(collection.GetType()); Debug.Assert(entityType != null, "Collection must at least be inherited from DataServiceCollection<T>"); if (!typeof(INotifyPropertyChanged).IsAssignableFrom(entityType)) { throw new InvalidOperationException(Strings.DataBinding_NotifyPropertyChangedNotImpl(entityType)); } typeof(BindingGraph) .GetMethod("SetObserver", BindingFlags.Instance | BindingFlags.NonPublic) .MakeGenericMethod(entityType) .Invoke(this, new object[] { collectionItf }); } else { this.graph.Root = collectionVertex; } Debug.Assert( collectionVertex.Parent != null || collectionVertex.IsRootCollection, "If parent is null, then collectionVertex should be a root collection"); this.AttachCollectionNotification(collection); foreach (var item in collectionItf) { this.AddEntity( source, sourceProperty, item, collectionEntitySet, collection); } return(true); }
public bool AddEntity( object source, string sourceProperty, object target, string targetEntitySet, object edgeSource) { Vertex sourceVertex = this.graph.LookupVertex(edgeSource); Debug.Assert(sourceVertex != null, "Must have a valid edge source"); Vertex entityVertex = null; bool addedNewEntity = false; if (target != null) { entityVertex = this.graph.LookupVertex(target); if (entityVertex == null) { entityVertex = this.graph.AddVertex(target); entityVertex.EntitySet = BindingEntityInfo.GetEntitySet(target, targetEntitySet); if (!this.AttachEntityOrComplexObjectNotification(target)) { throw new InvalidOperationException(Strings.DataBinding_NotifyPropertyChangedNotImpl(target.GetType())); } addedNewEntity = true; } if (this.graph.ExistsEdge(edgeSource, target, sourceVertex.IsCollection ? null : sourceProperty)) { throw new InvalidOperationException(Strings.DataBinding_EntityAlreadyInCollection(target.GetType())); } this.graph.AddEdge(edgeSource, target, sourceVertex.IsCollection ? null : sourceProperty); } if (!sourceVertex.IsCollection) { this.observer.HandleUpdateEntityReference( source, sourceProperty, sourceVertex.EntitySet, target, entityVertex == null ? null : entityVertex.EntitySet); } else { Debug.Assert(target != null, "Target must be non-null when adding to collections"); this.observer.HandleAddEntity( source, sourceProperty, sourceVertex.Parent != null ? sourceVertex.Parent.EntitySet : null, edgeSource as ICollection, target, entityVertex.EntitySet); } if (addedNewEntity) { this.AddFromProperties(target); } return(addedNewEntity); }
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.GetType() == typeof(ODataUriNullValue)) { 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.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 or complex type as a collection item type. case EdmTypeKind.Primitive: 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 // EdmTypeKind.Enum. throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } // In the released WCF Data Services 5.0, we didn't pass the model for JSON Verbose literals, so continuing that behavior for backward compatibility ODataFormat literalFormat = this.requestInfo.Format.UriLiteralFormat; IEdmModel edmModel = literalFormat == ODataFormat.VerboseJson ? null : this.requestInfo.Model; // ODL can handle null values so we can send null values here. string literal = ODataUriUtils.ConvertToUriLiteral(valueInODataFormat, CommonUtil.ConvertToODataVersion(this.requestInfo.MaxProtocolVersionAsVersion), edmModel, literalFormat); // 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> /// Writes the body operation parameters associated with a ServiceAction. /// </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 (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: { Collections.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: { collectionWriter.WriteItem(collectionItem); break; } default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference // EdmTypeKind.Enum. 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: parameterWriter.WriteValue(operationParameter.Name, operationParameter.Value); break; default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference // EdmTypeKind.Enum. throw new NotSupportedException(Strings.Serializer_InvalidParameterType(operationParameter.Name, edmType.TypeKind)); } } // else } // foreach parameterWriter.WriteEnd(); parameterWriter.Flush(); } }