/// <summary>
            /// Returns the structural property for the given payload element (only for properties).
            /// </summary>
            /// <param name="expectedTypeAnnotation">The expected type annotation.</param>
            /// <param name="model">The model to get the structural property from.</param>
            /// <returns>The expected structural property for the specified payload element.</returns>
            private static IEdmStructuralProperty GetExpectedStructuralProperty(ExpectedTypeODataPayloadElementAnnotation expectedTypeAnnotation, IEdmModel model)
            {
                ExceptionUtilities.Assert(model != null, "model != null");

                if (expectedTypeAnnotation != null)
                {
                    if (expectedTypeAnnotation.EdmProperty != null)
                    {
                        return(expectedTypeAnnotation.EdmProperty as IEdmStructuralProperty);
                    }
                    MemberProperty expectedStructuralProperty = expectedTypeAnnotation.MemberProperty;
                    if (expectedStructuralProperty != null)
                    {
                        NamedStructuralType expectedOwningType = expectedTypeAnnotation.OwningType;
                        ExceptionUtilities.Assert(expectedOwningType != null, "Need an owning type if a structural property is specified.");

                        IEdmStructuredType owningType = model.FindType(expectedOwningType.FullName) as IEdmStructuredType;
                        ExceptionUtilities.Assert(owningType != null, "Did not find expected structured type in the model.");

                        IEdmStructuralProperty structuralProperty = owningType.FindProperty(expectedStructuralProperty.Name) as IEdmStructuralProperty;
                        ExceptionUtilities.Assert(structuralProperty != null, "Did not find expected structural property in the model.");

                        return(structuralProperty);
                    }
                }

                return(null);
            }
            /// <summary>
            /// Returns the function import for a parameters payload.
            /// </summary>
            /// <param name="expectedTypeAnnotation">The expected type annotation.</param>
            /// <param name="model">The model to get the function import from.</param>
            /// <returns>Returns the function import for a parameters payload.</returns>
            private static IEdmOperationImport GetExpectedFunctionImport(ExpectedTypeODataPayloadElementAnnotation expectedTypeAnnotation, IEdmModel model)
            {
                ExceptionUtilities.Assert(model != null, "model != null");

                if (expectedTypeAnnotation != null)
                {
                    if (expectedTypeAnnotation.ProductFunctionImport != null)
                    {
                        return(expectedTypeAnnotation.ProductFunctionImport);
                    }
                    FunctionImport functionImport = expectedTypeAnnotation.FunctionImport;
                    if (functionImport != null)
                    {
                        var container       = model.FindEntityContainer(functionImport.Container.FullName);
                        var functionImports = container.FindOperationImports(functionImport.Name);
                        if (functionImports != null && functionImports.Any())
                        {
                            // Note that we don't support overload for Actions. Single() will throw if the model is invalid.
                            return(functionImports.Single());
                        }
                    }
                }

                return(null);
            }
            /// <summary>
            /// Returns the entity set for the given payload element (only for entries and feeds).
            /// </summary>
            /// <param name="expectedTypeAnnotation">The expected type annotation.</param>
            /// <param name="model">The model to get the type from.</param>
            /// <param name="payloadElement">The payload element to get the expected type for.</param>
            /// <returns>The expected type for the specified payload element.</returns>
            private static IEdmEntitySet GetExpectedEntitySet(ExpectedTypeODataPayloadElementAnnotation expectedTypeAnnotation, IEdmModel model, ODataPayloadElement payloadElement)
            {
                ExceptionUtilities.Assert(model != null, "model != null");
                ExceptionUtilities.Assert(payloadElement != null, "payloadElement != null");

                if (payloadElement.GetAnnotation <IgnoreEntitySetAnnotation>() != null)
                {
                    // Entity set information is explicitly ignored
                    return(null);
                }

                if (expectedTypeAnnotation != null)
                {
                    if (expectedTypeAnnotation.EdmEntitySet != null)
                    {
                        return(expectedTypeAnnotation.EdmEntitySet);
                    }
                    EntitySet entitySet = expectedTypeAnnotation.EntitySet;
                    if (entitySet != null)
                    {
                        return(model.EntityContainersAcrossModels().Select(m => m.FindEntitySet(entitySet.Name)).FirstOrDefault(s => s != null));
                    }
                }

                EntityModelTypeAnnotation typeAnnotation = payloadElement.GetAnnotation <EntityModelTypeAnnotation>();

                if (typeAnnotation != null)
                {
                    var edmEntityType = typeAnnotation.EdmModelType;
                    return(model.EntityContainersAcrossModels().First().EntitySets().SingleOrDefault(es => es.EntityType().FullName() == edmEntityType.FullName()));
                }

                return(null);
            }
            /// <summary>
            /// Returns the expected type for the given payload element.
            /// </summary>
            /// <param name="expectedTypeAnnotation">The expected type annotation.</param>
            /// <param name="model">The model to get the type from.</param>
            /// <returns>The expected type for the specified payload element.</returns>
            private static IEdmTypeReference GetExpectedType(ExpectedTypeODataPayloadElementAnnotation expectedTypeAnnotation, IEdmModel model)
            {
                if (expectedTypeAnnotation != null)
                {
                    if (expectedTypeAnnotation.EdmExpectedType != null)
                    {
                        return(expectedTypeAnnotation.EdmExpectedType);
                    }
                    if (expectedTypeAnnotation.ExpectedType != null)
                    {
                        DataType expectedDataType = expectedTypeAnnotation.ExpectedType;
                        return(EdmModelUtils.ResolveEntityModelSchemaType(model, expectedDataType));
                    }
                }

                return(null);
            }
        private void AddExpectedFunctionImportToCollection(ODataPayloadElementCollection collection)
        {
            var expectedTypeAnnotation = collection.GetAnnotation <ExpectedTypeODataPayloadElementAnnotation>();

            if (expectedTypeAnnotation == null)
            {
                expectedTypeAnnotation = new ExpectedTypeODataPayloadElementAnnotation();
                collection.Add(expectedTypeAnnotation);
            }

            if (expectedTypeAnnotation.ProductFunctionImport == null)
            {
                var typeAnnotation = collection.GetAnnotation <EntityModelTypeAnnotation>();
                var collectionType = typeAnnotation.EdmModelType;

                if (this.testDescriptor.PayloadEdmModel != null)
                {
                    EdmModel           model     = this.testDescriptor.PayloadEdmModel as EdmModel;
                    EdmEntityContainer container = model.EntityContainer as EdmEntityContainer;
                    var functionImport           = container.OperationImports().FirstOrDefault(f =>
                                                                                               { return(f.Operation.ReturnType != null && f.Operation.ReturnType == collectionType); });
                    if (functionImport == null)
                    {
                        functionImport = container.OperationImports().FirstOrDefault(f =>
                                                                                     { return(f.Operation.ReturnType != null && f.Operation.ReturnType.IsCollection()); });

                        if (functionImport == null)
                        {
                            var collectionNameAnnotation = collection.GetAnnotation <CollectionNameAnnotation>();
                            container.AddFunctionAndFunctionImport(model, collectionNameAnnotation == null ? "NewFunctionImport" : collectionNameAnnotation.Name,
                                                                   collectionType);
                            this.testDescriptor.ResetCachedModel();
                        }
                    }

                    expectedTypeAnnotation.ProductFunctionImport = functionImport as EdmOperationImport;
                }
            }
        }
        /// <summary>
        /// Adds or modifies a property's ExpectedTypeODataPayloadElementAnnotation, to aid generation of the context uri.
        /// </summary>
        /// <param name="property">The property to annotate.</param>
        /// <param name="propertyValueType">The type of the property's value.</param>
        /// <param name="matchesProperty">Delegate for matching the property instance to a MemberProperty.</param>
        /// <remarks>
        /// If the method cannot resolve the parent type of the property, one will be created and added to the test descriptor's
        /// PayloadModel. The descriptor's cached model will be reset.
        /// </remarks>
        private void AddExpectedTypeToProperty(PropertyInstance property, IEdmTypeReference propertyValueType, Func <IEdmProperty, bool> matchesProperty)
        {
            if (property.Annotations.OfType <JsonLightContextUriAnnotation>().Any())
            {
                return;
            }

            var typeAnnotation = property.Annotations.OfType <ExpectedTypeODataPayloadElementAnnotation>().SingleOrDefault();

            if (typeAnnotation == null || (typeAnnotation.MemberProperty == null && string.IsNullOrEmpty(typeAnnotation.OpenMemberPropertyName)))
            {
                ExpectedTypeODataPayloadElementAnnotation annotation = typeAnnotation ?? new ExpectedTypeODataPayloadElementAnnotation();

                IEdmModel model = this.testDescriptor.PayloadEdmModel;

                var entityType = model.EntityTypes().SingleOrDefault(t => t.Properties().Any(matchesProperty));
                if (entityType != null)
                {
                    annotation.EdmEntitySet  = FindEntitySet(model, entityType);
                    annotation.EdmOwningType = entityType;
                    annotation.EdmProperty   = entityType.Properties().FirstOrDefault(matchesProperty);
                }
                else
                {
                    var complexType = model.SchemaElements.OfType <IEdmComplexType>().SingleOrDefault(t => t.Properties().Any(matchesProperty));
                    if (complexType != null)
                    {
                        var complexProperty = complexType.Properties().Single(p => p.Name == property.Name);

                        annotation.EdmOwningType   = complexType;
                        annotation.EdmProperty     = complexProperty;
                        annotation.EdmExpectedType = complexProperty.Type;
                    }
                    else
                    {
                        // Add new entity type to the model and use that
                        IEdmTypeReference propertyType = annotation.EdmExpectedType ?? propertyValueType;

                        EdmEntityType newEntityType   = model.FindDeclaredType("TestModel.NewType") as EdmEntityType;
                        IEdmEntitySet newEntitySet    = null;
                        IEdmProperty  newProperty     = null;
                        string        newPorpertyName = property.Name ?? propertyType.FullName() ?? "EmptyName";

                        if (newEntityType == null)
                        {
                            newEntityType = new EdmEntityType("TestModel", "NewType");
                            newProperty   = newEntityType.AddStructuralProperty(newPorpertyName, propertyType);
                            ((EdmModel)model).AddElement(newEntityType);
                            var container = model.EntityContainersAcrossModels().Single() as EdmEntityContainer;
                            newEntitySet = container.AddEntitySet("NewTypes", newEntityType);
                        }
                        else
                        {
                            newProperty  = newEntityType.AddStructuralProperty(newPorpertyName, propertyType);
                            newEntitySet = FindEntitySet(model, newEntityType);
                        }

                        annotation.EdmEntitySet    = newEntitySet;
                        annotation.EdmOwningType   = newEntityType;
                        annotation.EdmProperty     = newProperty;
                        annotation.EdmExpectedType = propertyType;

                        this.testDescriptor.PayloadEdmModel = model;
                        this.testDescriptor.ResetCachedModel();
                    }
                }

                property.SetAnnotation(annotation);
            }
        }
        /// <summary>
        /// Builds a context URI from the expected type annotation.
        /// </summary>
        /// <param name="payloadElementKind">The payload element kind to build the context URI for.</param>
        /// <param name="metadataDocumentUri">The metadata document URI.</param>
        /// <param name="expectedTypeAnnotation">The expected type annotation.</param>
        /// <returns>The constructed context URI.</returns>
        public static string BuildContextUri(ODataPayloadElementType payloadElementKind, string metadataDocumentUri, ExpectedTypeODataPayloadElementAnnotation expectedTypeAnnotation, string projectionString = null)
        {
            ExceptionUtilities.CheckArgumentNotNull(metadataDocumentUri, "metadataDocumentUri");
            ExceptionUtilities.CheckArgumentNotNull(expectedTypeAnnotation, "expectedTypeAnnotation");

            StringBuilder builder = new StringBuilder(metadataDocumentUri);

            switch (payloadElementKind)
            {
            // Entry payload
            case ODataPayloadElementType.EntityInstance:
            {
                EdmEntitySet entitySet = (EdmEntitySet)expectedTypeAnnotation.EdmEntitySet;
                ExceptionUtilities.Assert(entitySet != null, "Entity set is required for entry payloads.");

                builder.Append('#');
                AppendEntityContainerElement(builder, entitySet.Container, entitySet.Name);
                AppendTypeCastIfNeeded(builder, entitySet, expectedTypeAnnotation.EdmExpectedType.Definition);
                builder.Append(projectionString);
                builder.Append("/$entity");

                break;
            }

            // Feed payload
            case ODataPayloadElementType.EntitySetInstance:
            {
                EdmEntitySet entitySet = (EdmEntitySet)expectedTypeAnnotation.EdmEntitySet;
                ExceptionUtilities.Assert(entitySet != null, "Entity set is required for feed payloads.");
                builder.Append('#');
                AppendEntityContainerElement(builder, entitySet.Container, entitySet.Name);
                AppendTypeCastIfNeeded(builder, entitySet, expectedTypeAnnotation.EdmExpectedType.Definition);
                builder.Append(projectionString);

                break;
            }

            // Property payload
            case ODataPayloadElementType.PrimitiveProperty:                 // fall through
            case ODataPayloadElementType.PrimitiveMultiValueProperty:       // fall through
            case ODataPayloadElementType.ComplexMultiValueProperty:         // fall through
            case ODataPayloadElementType.ComplexProperty:                   // fall through
            case ODataPayloadElementType.NullPropertyInstance:
            // Collection payload
            case ODataPayloadElementType.EmptyCollectionProperty:       // fall through
            case ODataPayloadElementType.ComplexInstanceCollection:     // fall through
            case ODataPayloadElementType.PrimitiveCollection:
                builder.Append('#');

                // NOTE: property payloads can be produced by regular properties as well as function imports
                IEdmTypeReference edmExpectedType = null;
                if (expectedTypeAnnotation.EdmProperty != null)
                {
                    edmExpectedType = expectedTypeAnnotation.EdmProperty.Type;
                }
                else if (expectedTypeAnnotation.ProductFunctionImport != null)
                {
                    edmExpectedType = expectedTypeAnnotation.ProductFunctionImport.Operation.ReturnType;
                }
                else if (expectedTypeAnnotation.EdmExpectedType != null)
                {
                    edmExpectedType = expectedTypeAnnotation.EdmExpectedType;
                }

                if (edmExpectedType == null)
                {
                    if (expectedTypeAnnotation.EdmExpectedType != null)
                    {
                        AppendTypeName(builder, expectedTypeAnnotation.EdmExpectedType.Definition);
                    }
                    else if (expectedTypeAnnotation.ProductFunctionImport != null)
                    {
                        AppendTypeName(builder, expectedTypeAnnotation.ProductFunctionImport.Operation.ReturnType.Definition);
                    }
                }
                else
                {
                    AppendTypeName(builder, edmExpectedType.Definition);
                }

                break;

            // Entity reference link payload
            case ODataPayloadElementType.DeferredLink:
            case ODataPayloadElementType.LinkCollection:
            {
                IEdmEntitySet         entitySet          = expectedTypeAnnotation.EdmEntitySet;
                EdmNavigationProperty navigationProperty = expectedTypeAnnotation.EdmNavigationProperty as EdmNavigationProperty;
                IEdmEntityType        entityType         = navigationProperty.DeclaringEntityType;

                ExceptionUtilities.Assert(entitySet != null, "entitySet is required for entity reference link payloads.");
                ExceptionUtilities.Assert(navigationProperty != null, "Navigation property is required for entity reference link payloads.");

                builder.Append('#');

                if (payloadElementKind == ODataPayloadElementType.DeferredLink)
                {
                    builder.Append("$ref");
                }
                else if (payloadElementKind == ODataPayloadElementType.LinkCollection)
                {
                    builder.Append("Collection($ref)");
                }

                break;
            }

            // Service document payload
            case ODataPayloadElementType.ServiceDocumentInstance:       // fall through
            case ODataPayloadElementType.WorkspaceInstance:
                // NOTE: the builder already contains the metadata document URI.
                break;

            default:
                return(null);
            }

            return(builder.ToString());
        }
        /// <summary>
        /// Gets the context URI for the specified ODataPayloadElement.
        /// </summary>
        /// <param name="payloadElement">The annotated payload element to get the context URI for.</param>
        /// <returns>The context URI from the annotated payload element or null if no context URI annotation exists.</returns>
        /// <remarks>If not context URI annotation is found on the <paramref name="payloadElement"/>, this
        /// method will try to compute the context URI from the expected type annotation. If successful,
        /// it will cache the computed context URI as annotation on the payload element.</remarks>
        public static string ContextUri(this ODataPayloadElement payloadElement)
        {
            ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
            JsonLightContextUriAnnotation contextUriAnnotation = (JsonLightContextUriAnnotation)payloadElement.GetAnnotation(typeof(JsonLightContextUriAnnotation));

            string contextUri      = null;
            bool   cacheContextUri = false;

            // If an explicit context URI exists, return it
            if (contextUriAnnotation != null)
            {
                contextUri = contextUriAnnotation.ContextUri;
            }
            else
            {
                // Otherwise construct a context URI from the expected type annotation
                ExpectedTypeODataPayloadElementAnnotation expectedTypeAnnotation =
                    (ExpectedTypeODataPayloadElementAnnotation)payloadElement.GetAnnotation(typeof(ExpectedTypeODataPayloadElementAnnotation));
                if (expectedTypeAnnotation != null)
                {
                    // Construct a context URI from the exptected type annotation
                    JsonLightMetadataDocumentUriAnnotation metadataDocumentUriAnnotation =
                        (JsonLightMetadataDocumentUriAnnotation)payloadElement.GetAnnotation(typeof(JsonLightMetadataDocumentUriAnnotation));
                    string metadataDocumentUri = metadataDocumentUriAnnotation == null
                        ? JsonLightConstants.DefaultMetadataDocumentUri.AbsoluteUri
                        : metadataDocumentUriAnnotation.MetadataDocumentUri;

                    string projectionString = null;
                    JsonLightContextUriProjectionAnnotation contextUriProjectionAnnotation = (JsonLightContextUriProjectionAnnotation)payloadElement.GetAnnotation(typeof(JsonLightContextUriProjectionAnnotation));
                    if (contextUriProjectionAnnotation != null)
                    {
                        // If we have a context URI projection, apply it to the context URI if the context URI does not already have one.
                        projectionString = contextUriProjectionAnnotation.ContextUriProjection;
                        Regex contextUriSelectExpandPattern = new Regex(@"(?:(?<!#Collection))\(.*\)");

                        // A 'null' projection string means that all properties should be projected.
                        if (projectionString != null)
                        {
                            bool hasProjection = contextUriSelectExpandPattern.IsMatch(projectionString);
                            if (!hasProjection)
                            {
                                // Inject the projection string into the context URI
                                projectionString = JsonLightConstants.ContextUriProjectionStart + projectionString + JsonLightConstants.ContextUriProjectionEnd;
                            }
                        }
                    }

                    contextUri      = BuildContextUri(payloadElement.ElementType, metadataDocumentUri, expectedTypeAnnotation, projectionString);
                    cacheContextUri = true;
                }
            }

            // Cache the computed context URI on the payload element (non-comparable annotation)
            if (cacheContextUri)
            {
                payloadElement.WithContextUri(contextUri);
                payloadElement.RemoveAnnotations(typeof(JsonLightContextUriProjectionAnnotation));
            }

            return(contextUri);
        }