/// <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); } } }
/// <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))); } } } } } }
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())); } }
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())); }