/// <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);
        }
Esempio n. 3
0
        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));
        }
Esempio n. 5
0
        /// <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);
        }