/// <summary> /// Adds an entity set backed by the <paramref name="resourceSet"/> to the entity container. /// </summary> /// <param name="entitySetName">The name of the entity set.</param> /// <param name="resourceSet">The resource set backing the entity set to be created.</param> /// <returns>an instance of IEdmEntitySet that just got added.</returns> /// <remarks> /// This method will also create the association sets and associations for the entity set. /// Materialization state: EntityContainers required. No change in materialization state. /// </remarks> internal IEdmEntitySet AddEntitySet(string entitySetName, ResourceSetWrapper resourceSet) { Debug.Assert(!string.IsNullOrEmpty(entitySetName), "!string.IsNullOrEmpty(entitySetName)"); Debug.Assert(resourceSet != null, "resourceSet != null"); IEdmEntitySet entitySet = new MetadataProviderEdmEntitySet(this.model, this, resourceSet); MetadataProviderUtils.ConvertCustomAnnotations(this.model, resourceSet.CustomAnnotations, entitySet); this.entitySetCache.Add(entitySetName, entitySet); return(entitySet); }
/// <summary> /// Tries to create a type name segment if the given identifier refers to a known type. /// </summary> /// <param name="previous">previous segment info.</param> /// <param name="segment">The segment being interpreted.</param> /// <param name="typeNameSegment">The type name segment, if one was created.</param> /// <returns>Whether or not a type segment was created for the identifier.</returns> private bool TryCreateTypeNameSegment(SegmentInfo previous, ODataPathSegment segment, out SegmentInfo typeNameSegment) { var typeSegment = segment as TypeSegment; if (typeSegment == null || previous.TargetResourceSet == null) { typeNameSegment = null; return(false); } ResourceType targetResourceType = MetadataProviderUtils.GetResourceType(typeSegment); // if the new type segment prevents any results from possibly being returned, then short-circuit and throw a 404. ResourceType previousResourceType = previous.TargetResourceType; Debug.Assert(previousResourceType != null, "previous.TargetResourceType != null"); if (!targetResourceType.IsAssignableFrom(previousResourceType) && !previousResourceType.IsAssignableFrom(targetResourceType)) { throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_InvalidTypeIdentifier_UnrelatedType(targetResourceType.FullName, previousResourceType.FullName)); } // Since we allow derived navigation properties or named streams in V1/V2, the server will generate edit links and navigation links with type segment in it. // Hence we need to be able to process type segment in the request even when the server MPV is set to V1/V2. But we do not want to expose new functionality // like filtering collections based on type, etc on V1/V2 servers. Hence only checking for MPV to be v3 or greater if the previous segment is a collection if (!previous.SingleResult) { VersionUtil.CheckMaxProtocolVersion(VersionUtil.Version4Dot0, this.maxProtocolVersion); } typeNameSegment = new SegmentInfo { Identifier = targetResourceType.FullName, Operation = previous.Operation, TargetKind = previous.TargetKind, TargetSource = previous.TargetSource, TargetResourceType = targetResourceType, SingleResult = previous.SingleResult, TargetResourceSet = previous.TargetResourceSet, ProjectedProperty = previous.ProjectedProperty, Key = previous.Key, RequestExpression = previous.RequestExpression, RequestEnumerable = previous.RequestEnumerable, IsTypeIdentifierSegment = true }; return(true); }
protected override object Read(System.Data.Services.SegmentInfo segmentInfo) { ResourceProperty projectedProperty; ResourceType resourceType; IEdmTypeReference typeReference; if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty) { projectedProperty = null; resourceType = null; typeReference = null; } else { projectedProperty = segmentInfo.ProjectedProperty; resourceType = projectedProperty.ResourceType; typeReference = base.GetTypeReference(resourceType, projectedProperty.CustomAnnotations.ToList <KeyValuePair <string, object> >()); if ((projectedProperty.Kind == ResourcePropertyKind.Primitive) && MetadataProviderUtils.ShouldDisablePrimitivePropertyNullValidation(projectedProperty, (IEdmPrimitiveTypeReference)typeReference)) { typeReference = base.GetSchemaType(resourceType).ToTypeReference(true); } if (((projectedProperty.Kind == ResourcePropertyKind.ComplexType) && base.Service.Provider.IsV1Provider) && !typeReference.IsNullable) { typeReference = base.GetSchemaType(resourceType).ToTypeReference(true); } } ODataProperty property2 = base.MessageReader.ReadProperty(typeReference); if ((((this.ContentFormat != ContentFormat.PlainXml) || ((segmentInfo.TargetKind != RequestTargetKind.OpenProperty) && (property2.Value != null))) && ((this.ContentFormat != ContentFormat.VerboseJson) || (property2.Name.Length != 0))) && (string.CompareOrdinal(segmentInfo.Identifier, property2.Name) != 0)) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.PlainXml_IncorrectElementName(segmentInfo.Identifier, property2.Name)); } object odataValue = property2.Value; if ((segmentInfo.TargetKind == RequestTargetKind.OpenProperty) && (odataValue is ODataCollectionValue)) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.BadRequest_OpenCollectionProperty(property2.Name)); } object obj3 = base.ConvertValue(odataValue, ref resourceType); if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty) { segmentInfo.TargetResourceType = resourceType; } return(obj3); }
/// <summary> /// Load entity set from model's metadata provider. /// </summary> /// <param name="qualifiedName">The name of the entity set to be loaded.</param> /// <returns>Entity set that is loaded.</returns> internal IEdmEntitySet LazyLoadEntitySet(string qualifiedName) { string entitySetName; var resourceSet = this.model.MetadataProvider.TryResolveResourceSet(qualifiedName); if (resourceSet == null) { // key of ResourceSet cache maybe containerName.entitySetName or only entitySetName var resourceSetWithContainerQualifiedName = this.model.MetadataProvider.TryResolveResourceSet(this.containerName + "." + qualifiedName); if (resourceSetWithContainerQualifiedName == null) { return(null); } entitySetName = MetadataProviderUtils.GetEntitySetName(resourceSetWithContainerQualifiedName.ResourceSet); return(this.AddEntitySet(entitySetName, resourceSetWithContainerQualifiedName)); } entitySetName = MetadataProviderUtils.GetEntitySetName(resourceSet.ResourceSet); return(this.AddEntitySet(entitySetName, resourceSet)); }
/// <summary> /// Reads the input request payload and returns the WCF DS value representation of it. /// </summary> /// <param name="segmentInfo">Info about the request to read.</param> /// <returns>The WCF DS representation of the value read.</returns> protected override object Read(SegmentInfo segmentInfo) { Debug.Assert(segmentInfo != null, "segmentInfo != null"); Debug.Assert( segmentInfo.TargetKind == RequestTargetKind.Primitive || segmentInfo.TargetKind == RequestTargetKind.ComplexObject || segmentInfo.TargetKind == RequestTargetKind.Collection || segmentInfo.TargetKind == RequestTargetKind.OpenProperty, "The PropertyDeserializer only supports Primitive, ComplexObject, Collection or OpenProperty target kinds."); ResourceProperty resourceProperty; ResourceType propertyResourceType; IEdmTypeReference propertyTypeReference; if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty) { resourceProperty = null; propertyResourceType = null; propertyTypeReference = null; } else { resourceProperty = segmentInfo.ProjectedProperty; propertyResourceType = resourceProperty.ResourceType; propertyTypeReference = this.GetTypeReference(propertyResourceType, resourceProperty.CustomAnnotations.ToList()); // WCF DS server allows null values for primitive properties. For non top-level properties, custom annotations are added // to ensure ODataLib does not perform any validation. The provider can choose to throw in which case, the response code will be 500. The // same applies to top-level primitive properties. So, we ensure the type reference carries the correct value for the nullability facet. if (resourceProperty.Kind == ResourcePropertyKind.Primitive && MetadataProviderUtils.ShouldDisablePrimitivePropertyNullValidation(resourceProperty, (IEdmPrimitiveTypeReference)propertyTypeReference)) { propertyTypeReference = this.GetSchemaType(propertyResourceType).ToTypeReference(true); } // WCF DS server allows null values for complex properties. For non top-level properties, custom annotations are added // to ensure ODataLib does not perform any validation. The provider can choose to throw in which case, the response code will be 500. The // same applies to top-level complex properties. So, we ensure the type reference carries the correct value for the nullability facet. if (resourceProperty.Kind == ResourcePropertyKind.ComplexType && this.Service.Provider.HasReflectionOrEFProviderQueryBehavior && !propertyTypeReference.IsNullable) { propertyTypeReference = this.GetSchemaType(propertyResourceType).ToTypeReference(true); } } ODataProperty property = this.MessageReader.ReadProperty(propertyTypeReference); Debug.Assert(property != null, "property != null"); #pragma warning disable 618 AssertReaderFormatIsExpected(this.MessageReader, ODataFormat.Atom, ODataFormat.Json); #pragma warning restore 618 // On V4, it seems this checking logic is useless. Will remove it after fully understanding. // PlainXmlDeserializer - PUT to a property with m:null='true' doesn't verify the name of the property matches // // PUT to open property with XML payload ignores the name of the property in the payload. // For backward compat reasons we must not fail if the property name doesn't match and the value is null or it's an open property (in XML case). // // V2 Server reads JSON complex values without property wrapper, ODataLib will report these as ODataProperty with empty name, so for those // we need to not compare the property name. // // For Json light, we do not validate the property name since in requests we do not parse the metadata URI and thus // will always report 'value'. if (!this.IsAtomRequest && !this.IsJsonLightRequest && string.CompareOrdinal(segmentInfo.Identifier, property.Name) != 0) { throw DataServiceException.CreateBadRequestError( Microsoft.OData.Service.Strings.PlainXml_IncorrectElementName(segmentInfo.Identifier, property.Name)); } object propertyValue = property.Value; object convertedValue = this.ConvertValue(propertyValue, ref propertyResourceType); if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty) { // Set the target resource type for open properties so we can reason over the property type later. // The target resource type of an open property segment is determined when converting the payload value above. Debug.Assert(segmentInfo.TargetResourceType == null, "segmentInfo.TargetResourceType == null"); segmentInfo.TargetResourceType = propertyResourceType; } else { Debug.Assert(segmentInfo.TargetResourceType.FullName == propertyResourceType.FullName, "segmentInfo.TargetResourceType == propertyResourceType"); } return(convertedValue); }
/// <summary> /// Load operation imports from model's metadata provider. /// </summary> /// <param name="qualifiedName">The name of the entity set to be loaded.</param> /// <returns>Operation imports that are loaded.</returns> internal List <IEdmOperationImport> LazyLoadServiceOperationImports(string qualifiedName) { List <IEdmOperationImport> operationImports = new List <IEdmOperationImport>(); OperationWrapper operationWrapper = this.model.MetadataProvider.TryResolveServiceOperation(qualifiedName); if (operationWrapper != null) { IEdmOperationImport foundOperationImport = this.model.EnsureDefaultEntityContainer().EnsureOperationImport(operationWrapper); if (foundOperationImport != null) { operationImports.Add(foundOperationImport); } } else { var operationWrapperQaulified = this.model.MetadataProvider.TryResolveServiceOperation(this.containerName + "." + qualifiedName); if (operationWrapperQaulified != null) { IEdmOperationImport foundOperationImport = this.model.EnsureDefaultEntityContainer().EnsureOperationImport(operationWrapperQaulified); if (foundOperationImport != null) { operationImports.Add(foundOperationImport); } } } // metadata interface in addition to the action provider interface. if (this.model.ActionProviderWrapper != null) { bool nameIsContainerQualified; var operationName = this.model.MetadataProvider.GetNameFromContainerQualifiedName(qualifiedName, out nameIsContainerQualified); var operation = this.model.ActionProviderWrapper.TryResolveServiceAction(operationName, MetadataProviderUtils.GetResourceType((IEdmType)null)); if (operation != null) { // Only top level actions will have an operation import. IEdmOperationImport foundOperationImport = this.model.EnsureDefaultEntityContainer().EnsureOperationImport(operation); if (foundOperationImport != null) { operationImports.Add(foundOperationImport); } } } return(operationImports); }
/// <summary> /// Returns whether ODataLib should be explicitly instructed to include or omit a type name on the wire. /// </summary> /// <param name="value">The value to be serialized.</param> /// <param name="actualType">The type to be potentially serialized.</param> /// <param name="typeNameToWrite">The type name which ODataLib should be told to serialize. A value of null indicates the type name should be omitted.</param> /// <returns>true if an annotation should be created to override ODataLib's default type name serialization behavior; false if the ODataLib default behavior should be used.</returns> internal bool ShouldSpecifyTypeNameAnnotation(ODataValue value, ResourceType actualType, out string typeNameToWrite) { if (this.metadataParameterValueForTypeNames == MetadataParameterValue.Full) { ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue; Debug.Assert(primitiveValue == null || actualType.ResourceTypeKind == ResourceTypeKind.Primitive, "If value is ODataPrimitiveValue, actualType must also be primitive."); if (primitiveValue != null && JsonSharedUtils.ValueTypeMatchesJsonType(primitiveValue, MetadataProviderUtils.CreatePrimitiveTypeReference(actualType))) { // Don't set the annotation and use the default ODataLib type name serialization behavior for basic JSON types. typeNameToWrite = null; return(false); } typeNameToWrite = actualType.FullName; return(true); } if (this.metadataParameterValueForTypeNames == MetadataParameterValue.None) { // Setting the type name to null explicitly tells ODataLib to not write a type name annotation on the wire. typeNameToWrite = null; return(true); } // Otherwise, don't set the annotation and use the default ODataLib type name serialization behavior. typeNameToWrite = null; return(false); }