/// <summary>
        /// Creates an <see cref="IEdmProperty"/> for the <paramref name="resourceProperty"/>.
        /// </summary>
        /// <param name="declaringType">The declaring type of the <paramref name="resourceProperty"/>.</param>
        /// <param name="resourceProperty">The resource property to create an EDM property for.</param>
        /// <returns>An <see cref="IEdmProperty"/> instance for the <paramref name="resourceProperty"/>.</returns>
        /// <remarks>
        /// Materialization state: none required. No change in materialization state.
        /// Cache state: none required. No change in cache state.
        /// </remarks>
        private IEdmProperty CreateProperty(EdmStructuredType declaringType, ResourceProperty resourceProperty)
        {
            Debug.Assert(declaringType != null, "structuralType != null");
            Debug.Assert(resourceProperty != null, "resourceProperty != null");

            List<KeyValuePair<string, object>> customAnnotations = resourceProperty.CustomAnnotations == null ? null : resourceProperty.CustomAnnotations.ToList();
            IEdmProperty result;
            ODataNullValueBehaviorKind nullValueReadBehaviorKind = ODataNullValueBehaviorKind.Default;
            if (resourceProperty.IsOfKind(ResourcePropertyKind.Primitive) || resourceProperty.IsOfKind(ResourcePropertyKind.Stream))
            {
                ResourceType resourceType = resourceProperty.ResourceType;
                IEdmPrimitiveTypeReference primitiveTypeReference = MetadataProviderUtils.CreatePrimitiveTypeReference(resourceType, customAnnotations);

                if (resourceProperty.IsOfKind(ResourcePropertyKind.Key))
                {
                    // if the property is a key we force nullable to be 'false'
                    if (primitiveTypeReference.IsNullable)
                    {
                        Debug.Assert(resourceProperty.IsOfKind(ResourcePropertyKind.Primitive), "Streams are always non-nullable so this must be a primitive resource property");
                        primitiveTypeReference = (IEdmPrimitiveTypeReference)primitiveTypeReference.Clone(/*nullable*/false);
                    }

                    // EdmLib bridge marks all key properties as non-nullable, but Astoria allows them to be nullable.
                    // Key properties must be non-nullable in CSDL; in Astoria we need the ability to ignore null values for 
                    // key properties since our client will send null values for non-initialized properties (e.g., insert with
                    // auto-generated keys).
                    // Note that this applies even to properties which are not nullable in Astoria, we still need to ignore null values
                    // for key property like that.
                    nullValueReadBehaviorKind = ODataNullValueBehaviorKind.IgnoreValue;
                }
                else if (MetadataProviderUtils.ShouldDisablePrimitivePropertyNullValidation(resourceProperty, primitiveTypeReference))
                {
                    nullValueReadBehaviorKind = ODataNullValueBehaviorKind.DisableValidation;
                }

                string defaultValue = MetadataProviderUtils.GetAndRemoveDefaultValue(customAnnotations);
                EdmConcurrencyMode concurrencyMode = resourceProperty.IsOfKind(ResourcePropertyKind.ETag)
                    ? EdmConcurrencyMode.Fixed
                    : EdmConcurrencyMode.None;
                result = new MetadataProviderEdmStructuralProperty(declaringType, resourceProperty, primitiveTypeReference, defaultValue, concurrencyMode);
                declaringType.AddProperty(result);

                string mimeType = resourceProperty.MimeType;
                if (!string.IsNullOrEmpty(mimeType))
                {
                    this.SetMimeType(result, mimeType);
                }
            }
            else if (resourceProperty.IsOfKind(ResourcePropertyKind.ComplexType))
            {
                IEdmTypeReference typeReference = this.EnsureTypeReference(resourceProperty.ResourceType, customAnnotations);

                // NOTE: WCF DS does not support complex ETag properties
                string defaultValue = MetadataProviderUtils.GetAndRemoveDefaultValue(customAnnotations);

                result = new MetadataProviderEdmStructuralProperty(declaringType, resourceProperty, typeReference, defaultValue, EdmConcurrencyMode.None);
                declaringType.AddProperty(result);

                // We need to mark all complex types in EF provider as "allow null" in order to maintain backward compat behavior
                // of failing with 500 on null updates to complex values (since WCF DS V2 treated all complex values as nullable).
                // Note that V1 providers are EF and Reflection. But reflection provider marks all complex types as nullable.
                if (this.metadataProvider.HasReflectionOrEFProviderQueryBehavior && !typeReference.IsNullable)
                {
                    nullValueReadBehaviorKind = ODataNullValueBehaviorKind.DisableValidation;
                }
            }
            else if (resourceProperty.IsOfKind(ResourcePropertyKind.Collection))
            {
                string defaultValue = MetadataProviderUtils.GetAndRemoveDefaultValue(customAnnotations);
                IEdmTypeReference collectionTypeReference = this.EnsureTypeReference(resourceProperty.ResourceType, customAnnotations);
                result = new MetadataProviderEdmStructuralProperty(declaringType, resourceProperty, collectionTypeReference, defaultValue, EdmConcurrencyMode.None);
                declaringType.AddProperty(result);
            }
            else if (resourceProperty.IsOfKind(ResourcePropertyKind.ResourceSetReference) || resourceProperty.IsOfKind(ResourcePropertyKind.ResourceReference))
            {
                Debug.Assert(resourceProperty.TypeKind == ResourceTypeKind.EntityType, "Expected entity type kind for navigation properties");
                Debug.Assert(declaringType.TypeKind == EdmTypeKind.Entity, "declaringType.TypeKind == EdmTypeKind.Entity");
                EdmEntityType sourceEntityType = (EdmEntityType)declaringType;

                IEdmTypeReference targetTypeReference = resourceProperty.IsOfKind(ResourcePropertyKind.ResourceSetReference)
                    ? this.EnsureEntityPrimitiveOrComplexCollectionTypeReference(resourceProperty.ResourceType, new EntityCollectionResourceType(resourceProperty.ResourceType), customAnnotations)
                    : this.EnsureTypeReference(resourceProperty.ResourceType, customAnnotations);
                result = new MetadataProviderEdmNavigationProperty(sourceEntityType, resourceProperty, targetTypeReference);
                sourceEntityType.AddProperty(result);
            }
            else
            {
                throw new InvalidOperationException(Microsoft.OData.Service.Strings.MetadataProviderEdmModel_UnsupportedResourcePropertyKind(resourceProperty.Kind.ToString()));
            }

            // Add the OData property annotation to the property
            // TODO: Once we want to support OData.Query we need to add a property annotation here to specify the CanReflectOnInstanceProperty value.
            this.SetNullValueReaderBehavior(result, nullValueReadBehaviorKind);

            // NOTE: we removed any facets from the annotations above; the remaining annotations will be serialized.
            MetadataProviderUtils.ConvertCustomAnnotations(this, customAnnotations, result);

            return result;
        }
        private IEdmProperty CreateStructuralTypeCollectionPropertyBody(EdmStructuredType type, CollectionPropertyConfiguration collectionProperty)
        {
            IEdmTypeReference elementTypeReference = null;
            Type clrType = TypeHelper.GetUnderlyingTypeOrSelf(collectionProperty.ElementType);

            if (clrType.IsEnum)
            {
                IEdmType edmType = GetEdmType(clrType);

                if (edmType == null)
                {
                    throw Error.InvalidOperation(SRResources.EnumTypeDoesNotExist, clrType.Name);
                }

                IEdmEnumType enumElementType = (IEdmEnumType)edmType;
                bool isNullable = collectionProperty.ElementType != clrType;
                elementTypeReference = new EdmEnumTypeReference(enumElementType, isNullable);
            }
            else
            {
                IEdmType edmType = GetEdmType(collectionProperty.ElementType);
                if (edmType != null)
                {
                    IEdmComplexType elementType = edmType as IEdmComplexType;
                    Contract.Assert(elementType != null);
                    elementTypeReference = new EdmComplexTypeReference(elementType, collectionProperty.OptionalProperty);
                }
                else
                {
                    elementTypeReference =
                        EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(collectionProperty.ElementType);
                    Contract.Assert(elementTypeReference != null);
                }
            }

            return type.AddStructuralProperty(
                collectionProperty.Name,
                new EdmCollectionTypeReference(new EdmCollectionType(elementTypeReference)));
        }
        private IEdmProperty CreateStructuralTypeEnumPropertyBody(EdmStructuredType type, StructuralTypeConfiguration config, EnumPropertyConfiguration enumProperty)
        {
            Type enumPropertyType = TypeHelper.GetUnderlyingTypeOrSelf(enumProperty.RelatedClrType);
            IEdmType edmType = GetEdmType(enumPropertyType);

            if (edmType == null)
            {
                throw Error.InvalidOperation(SRResources.EnumTypeDoesNotExist, enumPropertyType.Name);
            }

            IEdmEnumType enumType = (IEdmEnumType)edmType;
            IEdmTypeReference enumTypeReference = new EdmEnumTypeReference(enumType, enumProperty.OptionalProperty);

            // Set concurrency token if is entity type, and concurrency token is true
            EdmConcurrencyMode enumConcurrencyMode = EdmConcurrencyMode.None;
            if (config.Kind == EdmTypeKind.Entity && enumProperty.ConcurrencyToken)
            {
                enumConcurrencyMode = EdmConcurrencyMode.Fixed;
            }

            return type.AddStructuralProperty(
                enumProperty.Name,
                enumTypeReference,
                defaultValue: null,
                concurrencyMode: enumConcurrencyMode);
        }
        private void CreateStructuralTypeBody(EdmStructuredType type, StructuralTypeConfiguration config)
        {
            foreach (PropertyConfiguration property in config.Properties)
            {
                IEdmProperty edmProperty = null;

                switch (property.Kind)
                {
                    case PropertyKind.Primitive:
                        PrimitivePropertyConfiguration primitiveProperty = (PrimitivePropertyConfiguration)property;
                        EdmPrimitiveTypeKind typeKind = primitiveProperty.TargetEdmTypeKind ??
                                                        GetTypeKind(primitiveProperty.PropertyInfo.PropertyType);
                        IEdmTypeReference primitiveTypeReference = EdmCoreModel.Instance.GetPrimitive(
                            typeKind,
                            primitiveProperty.OptionalProperty);

                        // Set concurrency token if is entity type, and concurrency token is true
                        EdmConcurrencyMode concurrencyMode = EdmConcurrencyMode.None;
                        if (config.Kind == EdmTypeKind.Entity && primitiveProperty.ConcurrencyToken)
                        {
                            concurrencyMode = EdmConcurrencyMode.Fixed;
                        }
                        edmProperty = type.AddStructuralProperty(
                            primitiveProperty.Name,
                            primitiveTypeReference,
                            defaultValue: null,
                            concurrencyMode: concurrencyMode);
                        break;

                    case PropertyKind.Complex:
                        ComplexPropertyConfiguration complexProperty = property as ComplexPropertyConfiguration;
                        IEdmComplexType complexType = GetEdmType(complexProperty.RelatedClrType) as IEdmComplexType;

                        edmProperty = type.AddStructuralProperty(
                            complexProperty.Name,
                            new EdmComplexTypeReference(complexType, complexProperty.OptionalProperty));
                        break;

                    case PropertyKind.Collection:
                        edmProperty = CreateStructuralTypeCollectionPropertyBody(type, (CollectionPropertyConfiguration)property);
                        break;

                    case PropertyKind.Enum:
                        edmProperty = CreateStructuralTypeEnumPropertyBody(type, config, (EnumPropertyConfiguration)property);
                        break;

                    default:
                        break;
                }

                if (edmProperty != null)
                {
                    if (property.PropertyInfo != null)
                    {
                        _properties[property.PropertyInfo] = edmProperty;
                    }

                    if (property.IsRestricted)
                    {
                        _propertiesRestrictions[edmProperty] = new QueryableRestrictions(property);
                    }
                }
            }
        }
        private void CreateStructuralTypeBody(EdmStructuredType type, StructuralTypeConfiguration config)
        {
            foreach (PropertyConfiguration property in config.Properties)
            {
                IEdmProperty edmProperty = null;

                switch (property.Kind)
                {
                    case PropertyKind.Primitive:
                        PrimitivePropertyConfiguration primitiveProperty = property as PrimitivePropertyConfiguration;
                        EdmPrimitiveTypeKind typeKind = GetTypeKind(primitiveProperty.PropertyInfo.PropertyType);
                        IEdmTypeReference primitiveTypeReference = EdmCoreModel.Instance.GetPrimitive(
                            typeKind,
                            primitiveProperty.OptionalProperty);

                        // Set concurrency token if is entity type, and concurrency token is true
                        EdmConcurrencyMode concurrencyMode = EdmConcurrencyMode.None;
                        if (config.Kind == EdmTypeKind.Entity && primitiveProperty.ConcurrencyToken)
                        {
                            concurrencyMode = EdmConcurrencyMode.Fixed;
                        }
                        edmProperty = type.AddStructuralProperty(
                            primitiveProperty.Name,
                            primitiveTypeReference,
                            defaultValue: null,
                            concurrencyMode: concurrencyMode);
                        break;

                    case PropertyKind.Complex:
                        ComplexPropertyConfiguration complexProperty = property as ComplexPropertyConfiguration;
                        IEdmComplexType complexType = _types[complexProperty.RelatedClrType] as IEdmComplexType;

                        edmProperty = type.AddStructuralProperty(
                            complexProperty.Name,
                            new EdmComplexTypeReference(complexType, complexProperty.OptionalProperty));
                        break;

                    case PropertyKind.Collection:
                        CollectionPropertyConfiguration collectionProperty = property as CollectionPropertyConfiguration;
                        IEdmTypeReference elementTypeReference = null;
                        if (_types.ContainsKey(collectionProperty.ElementType))
                        {
                            IEdmComplexType elementType = _types[collectionProperty.ElementType] as IEdmComplexType;
                            elementTypeReference = new EdmComplexTypeReference(elementType, false);
                        }
                        else
                        {
                            elementTypeReference = EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(collectionProperty.ElementType);
                        }
                        edmProperty = type.AddStructuralProperty(
                            collectionProperty.Name,
                            new EdmCollectionTypeReference(
                                new EdmCollectionType(elementTypeReference),
                                collectionProperty.OptionalProperty));
                        break;

                    default:
                        break;
                }

                if (property.PropertyInfo != null && edmProperty != null)
                {
                    _properties[property.PropertyInfo] = edmProperty;
                }

                if (edmProperty != null && property.IsRestricted)
                {
                    _propertiesRestrictions[edmProperty] = new QueryableRestrictions(property);
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Creates metadata properties for the specified enumeration of OData OM properties.
        /// </summary>
        /// <param name="model">The model to use.</param>
        /// <param name="owningType">The owning type to which to add the properties.</param>
        /// <param name="properties">Enumeration of the properties to add.</param>
        private static void CreateMetadataProperties(ConstructableMetadata model, EdmStructuredType owningType, IEnumerable<ODataProperty> properties)
        {
            if (properties == null)
            {
                return;
            }

            EdmEntityType owningEntityType = owningType as EdmEntityType;
            foreach (ODataProperty property in properties)
            {
                ODataPropertyMetadataAnnotation propertyMetadataAnnotation = property.GetInheritedAnnotation<ODataPropertyMetadataAnnotation>();

                object propertyValue = property.Value;
                bool isOpenProperty = false;
                if (propertyMetadataAnnotation != null)
                {
                    isOpenProperty = propertyMetadataAnnotation.IsOpenProperty;
                    ExceptionUtilities.Assert(
                        !isOpenProperty || owningType is EdmEntityType,
                        "Can't declare an open property on non-entity type.");
                    if (propertyMetadataAnnotation.PropertyValueForTypeInference != null)
                    {
                        propertyValue = propertyMetadataAnnotation.PropertyValueForTypeInference;
                    }
                }
                ODataComplexValue complexValue = propertyValue as ODataComplexValue;
                if (complexValue != null)
                {
                    ProcessComplexProperty(model, owningType, complexValue, isOpenProperty);
                }
                else
                {
                    bool isKeyProperty = false;
                    if (propertyMetadataAnnotation != null)
                    {
                        isKeyProperty = (propertyMetadataAnnotation.Kind & ODataPropertyKind.Key) == ODataPropertyKind.Key;
                        bool isETagProperty = (propertyMetadataAnnotation.Kind & ODataPropertyKind.ETag) == ODataPropertyKind.ETag;

                        ExceptionUtilities.Assert(
                            !isKeyProperty || owningType is EdmEntityType,
                            "Can't declare a key property on non-entity type.");
                        ExceptionUtilities.Assert(
                            !isETagProperty || owningType is EdmEntityType,
                            "Can't declare an etag property on non-entity type.");
                        ExceptionUtilities.Assert(
                            !isOpenProperty || !(isKeyProperty || isETagProperty),
                            "Key or etag property can't be open.");
                    }

                    if (owningEntityType != null && isOpenProperty)
                    {
                        //TODO: figure out how to set entity type to open after creation.
                    }
                    else
                    {
                        if (owningEntityType != null && isKeyProperty)
                        {
                            ExceptionUtilities.Assert(propertyValue != null, 
                                "Found a key property with null value, can't infer type from value in that case and it's invalid anyway.");
                            var keyPropertyType = GetPrimitiveTypeReference(propertyValue.GetType());
                            var keyProperty = owningEntityType.AddStructuralProperty(property.Name, keyPropertyType);
                            owningEntityType.AddKeys(keyProperty);
                        }
                        else
                        {
                            if (propertyValue is ODataCollectionValue)
                            {
                                ProcessCollectionProperty(model, owningType, propertyValue, owningEntityType, property);
                            }
                            else
                            {
                                Type propertyType = propertyValue == null ? typeof(string) : propertyValue.GetType();
                                owningType.AddProperty(new EdmStructuralProperty(owningType, property.Name, GetPrimitiveTypeReference(propertyType)));
                            }
                        }
                    }
                }
            }
        }
Beispiel #7
0
        private static void ProcessComplexProperty(ConstructableMetadata model, EdmStructuredType owningType, ODataComplexValue complexValue, bool isOpenProperty)
        {
            string namespaceName = "";
            var schematype = owningType as IEdmSchemaType;
            if (schematype != null)
            {
                namespaceName = schematype.Namespace;
            }

            ExceptionUtilities.Assert(complexValue.TypeName.StartsWith(namespaceName + "."), "The type name must start with the same namespace as its owning type.");

            EdmComplexType complexType = model.FindType(complexValue.TypeName) as EdmComplexType;
            if (complexType == null)
            {
                string complexTypeLocalName = complexValue.GetUnqualifiedTypeName(namespaceName);
                complexType = model.AddComplexType(complexTypeLocalName, null, null, false) as EdmComplexType;
                CreateMetadataProperties(model, complexType, complexValue.Properties);
            }

            if (isOpenProperty)
            {
                //TODO: Find way to set parent type to open.
            }
            else
            {
                ExceptionUtilities.CheckObjectNotNull(complexType, "Complex type cannot be null");
                owningType.AddProperty(new EdmStructuralProperty(owningType, complexType.Name, complexType.ToTypeReference()));
            }
        }
Beispiel #8
0
        private static void ProcessCollectionProperty(ConstructableMetadata model, EdmStructuredType owningType, object propertyValue, EdmEntityType owningEntityType, ODataProperty property)
        {
            string itemTypeName = EdmModelUtils.GetCollectionItemTypeName(((ODataCollectionValue)propertyValue).TypeName);
            IEdmPrimitiveTypeReference primitiveItemType = MetadataUtils.GetPrimitiveTypeReference(itemTypeName);
            IEdmCollectionType collectionType = GetCollectionType(model, primitiveItemType, itemTypeName);

            ExceptionUtilities.Assert(collectionType != null, "Could not resolve item type.");
            owningType.AddProperty(new EdmStructuralProperty(owningType, property.Name, collectionType.ToTypeReference()));
        }