diff --git a/src/Microsoft.OData.ModelBuilder/Commons/EdmLibHelpers.cs b/src/Microsoft.OData.ModelBuilder/Commons/EdmLibHelpers.cs index 262ea8a..30d41e2 100644 --- a/src/Microsoft.OData.ModelBuilder/Commons/EdmLibHelpers.cs +++ b/src/Microsoft.OData.ModelBuilder/Commons/EdmLibHelpers.cs @@ -5,15 +5,13 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; -using System.IO; using System.Linq; -using System.Xml.Linq; using Microsoft.OData.Edm; using Microsoft.OData.Edm.Vocabularies; using Microsoft.OData.Edm.Vocabularies.V1; using Microsoft.OData.ModelBuilder.Annotations; using Microsoft.OData.ModelBuilder.Config; -using Microsoft.Spatial; +using Microsoft.OData.ModelBuilder.Providers; namespace Microsoft.OData.ModelBuilder { @@ -24,77 +22,34 @@ internal static class EdmLibHelpers { private static readonly EdmCoreModel _coreModel = EdmCoreModel.Instance; - private static readonly Dictionary _builtInTypesMapping = - new[] - { - new KeyValuePair(typeof(string), GetPrimitiveType(EdmPrimitiveTypeKind.String)), - new KeyValuePair(typeof(bool), GetPrimitiveType(EdmPrimitiveTypeKind.Boolean)), - new KeyValuePair(typeof(bool?), GetPrimitiveType(EdmPrimitiveTypeKind.Boolean)), - new KeyValuePair(typeof(byte), GetPrimitiveType(EdmPrimitiveTypeKind.Byte)), - new KeyValuePair(typeof(byte?), GetPrimitiveType(EdmPrimitiveTypeKind.Byte)), - new KeyValuePair(typeof(decimal), GetPrimitiveType(EdmPrimitiveTypeKind.Decimal)), - new KeyValuePair(typeof(decimal?), GetPrimitiveType(EdmPrimitiveTypeKind.Decimal)), - new KeyValuePair(typeof(double), GetPrimitiveType(EdmPrimitiveTypeKind.Double)), - new KeyValuePair(typeof(double?), GetPrimitiveType(EdmPrimitiveTypeKind.Double)), - new KeyValuePair(typeof(Guid), GetPrimitiveType(EdmPrimitiveTypeKind.Guid)), - new KeyValuePair(typeof(Guid?), GetPrimitiveType(EdmPrimitiveTypeKind.Guid)), - new KeyValuePair(typeof(short), GetPrimitiveType(EdmPrimitiveTypeKind.Int16)), - new KeyValuePair(typeof(short?), GetPrimitiveType(EdmPrimitiveTypeKind.Int16)), - new KeyValuePair(typeof(int), GetPrimitiveType(EdmPrimitiveTypeKind.Int32)), - new KeyValuePair(typeof(int?), GetPrimitiveType(EdmPrimitiveTypeKind.Int32)), - new KeyValuePair(typeof(long), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), - new KeyValuePair(typeof(long?), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), - new KeyValuePair(typeof(sbyte), GetPrimitiveType(EdmPrimitiveTypeKind.SByte)), - new KeyValuePair(typeof(sbyte?), GetPrimitiveType(EdmPrimitiveTypeKind.SByte)), - new KeyValuePair(typeof(float), GetPrimitiveType(EdmPrimitiveTypeKind.Single)), - new KeyValuePair(typeof(float?), GetPrimitiveType(EdmPrimitiveTypeKind.Single)), - new KeyValuePair(typeof(byte[]), GetPrimitiveType(EdmPrimitiveTypeKind.Binary)), - new KeyValuePair(typeof(Stream), GetPrimitiveType(EdmPrimitiveTypeKind.Stream)), - new KeyValuePair(typeof(Geography), GetPrimitiveType(EdmPrimitiveTypeKind.Geography)), - new KeyValuePair(typeof(GeographyPoint), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyPoint)), - new KeyValuePair(typeof(GeographyLineString), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyLineString)), - new KeyValuePair(typeof(GeographyPolygon), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyPolygon)), - new KeyValuePair(typeof(GeographyCollection), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyCollection)), - new KeyValuePair(typeof(GeographyMultiLineString), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyMultiLineString)), - new KeyValuePair(typeof(GeographyMultiPoint), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyMultiPoint)), - new KeyValuePair(typeof(GeographyMultiPolygon), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyMultiPolygon)), - new KeyValuePair(typeof(Geometry), GetPrimitiveType(EdmPrimitiveTypeKind.Geometry)), - new KeyValuePair(typeof(GeometryPoint), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryPoint)), - new KeyValuePair(typeof(GeometryLineString), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryLineString)), - new KeyValuePair(typeof(GeometryPolygon), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryPolygon)), - new KeyValuePair(typeof(GeometryCollection), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryCollection)), - new KeyValuePair(typeof(GeometryMultiLineString), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryMultiLineString)), - new KeyValuePair(typeof(GeometryMultiPoint), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryMultiPoint)), - new KeyValuePair(typeof(GeometryMultiPolygon), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryMultiPolygon)), - new KeyValuePair(typeof(DateTimeOffset), GetPrimitiveType(EdmPrimitiveTypeKind.DateTimeOffset)), - new KeyValuePair(typeof(DateTimeOffset?), GetPrimitiveType(EdmPrimitiveTypeKind.DateTimeOffset)), - new KeyValuePair(typeof(TimeSpan), GetPrimitiveType(EdmPrimitiveTypeKind.Duration)), - new KeyValuePair(typeof(TimeSpan?), GetPrimitiveType(EdmPrimitiveTypeKind.Duration)), - new KeyValuePair(typeof(Date), GetPrimitiveType(EdmPrimitiveTypeKind.Date)), - new KeyValuePair(typeof(Date?), GetPrimitiveType(EdmPrimitiveTypeKind.Date)), - new KeyValuePair(typeof(TimeOfDay), GetPrimitiveType(EdmPrimitiveTypeKind.TimeOfDay)), - new KeyValuePair(typeof(TimeOfDay?), GetPrimitiveType(EdmPrimitiveTypeKind.TimeOfDay)), - new KeyValuePair(typeof(DateOnly), GetPrimitiveType(EdmPrimitiveTypeKind.Date)), - new KeyValuePair(typeof(DateOnly?), GetPrimitiveType(EdmPrimitiveTypeKind.Date)), - new KeyValuePair(typeof(TimeOnly), GetPrimitiveType(EdmPrimitiveTypeKind.TimeOfDay)), - new KeyValuePair(typeof(TimeOnly?), GetPrimitiveType(EdmPrimitiveTypeKind.TimeOfDay)), - - // Keep the Binary and XElement in the end, since there are not the default mappings for Edm.Binary and Edm.String. - new KeyValuePair(typeof(XElement), GetPrimitiveType(EdmPrimitiveTypeKind.String)), - // new KeyValuePair(typeof(Binary), GetPrimitiveType(EdmPrimitiveTypeKind.Binary)), - new KeyValuePair(typeof(ushort), GetPrimitiveType(EdmPrimitiveTypeKind.Int32)), - new KeyValuePair(typeof(ushort?), GetPrimitiveType(EdmPrimitiveTypeKind.Int32)), - new KeyValuePair(typeof(uint), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), - new KeyValuePair(typeof(uint?), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), - new KeyValuePair(typeof(ulong), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), - new KeyValuePair(typeof(ulong?), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), - new KeyValuePair(typeof(char[]), GetPrimitiveType(EdmPrimitiveTypeKind.String)), - new KeyValuePair(typeof(char), GetPrimitiveType(EdmPrimitiveTypeKind.String)), - new KeyValuePair(typeof(char?), GetPrimitiveType(EdmPrimitiveTypeKind.String)), - new KeyValuePair(typeof(DateTime), GetPrimitiveType(EdmPrimitiveTypeKind.DateTimeOffset)), - new KeyValuePair(typeof(DateTime?), GetPrimitiveType(EdmPrimitiveTypeKind.DateTimeOffset)), - } - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + private static Lazy lazyPrimitiveTypeMappingProvider; + + private static IEdmTypeMappingProvider PrimitiveTypeMappingProvider + { + get + { + if (lazyPrimitiveTypeMappingProvider == null) + { + lazyPrimitiveTypeMappingProvider = new Lazy( + () => new DefaultEdmTypeMappingProvider()); + } + + return lazyPrimitiveTypeMappingProvider.Value; + } + } + + public static void ConfigurePrimitiveTypeMappingProvider(Func factory) + { + if (lazyPrimitiveTypeMappingProvider?.IsValueCreated == true) + { + return; + } + + if (lazyPrimitiveTypeMappingProvider == null) + { + lazyPrimitiveTypeMappingProvider = new Lazy(factory); + } + } public static bool HasLength(EdmPrimitiveTypeKind primitiveTypeKind) { @@ -142,18 +97,14 @@ public static Type GetClrType(this IEdmModel edmModel,IEdmTypeReference edmTypeR return typeof(object); } - Type primitiveClrType = _builtInTypesMapping - .Where(kvp => edmTypeReference.Definition.IsEquivalentTo(kvp.Value) && (!edmTypeReference.IsNullable || IsNullable(kvp.Key))) - .Select(kvp => kvp.Key) - .FirstOrDefault(); - - if (primitiveClrType != null) + Type clrType; + if (PrimitiveTypeMappingProvider.TryGetClrType(edmTypeReference, out clrType)) { - return primitiveClrType; + return clrType; } else { - Type clrType = edmModel.GetClrType(edmTypeReference.Definition, assembliesResolver); + clrType = edmModel.GetClrType(edmTypeReference.Definition, assembliesResolver); if (clrType != null && TypeHelper.IsEnum(clrType) && edmTypeReference.IsNullable) { return TypeHelper.ToNullable(clrType); @@ -196,8 +147,12 @@ public static Type GetClrType(this IEdmModel edmModel, IEdmType edmType, IAssemb public static IEdmPrimitiveType GetEdmPrimitiveTypeOrNull(Type clrType) { - IEdmPrimitiveType primitiveType; - return _builtInTypesMapping.TryGetValue(clrType, out primitiveType) ? primitiveType : null; + if (PrimitiveTypeMappingProvider.TryGetEdmType(clrType, out IEdmPrimitiveType primitiveType)) + { + return primitiveType; + } + + return null; } public static IEdmPrimitiveTypeReference GetEdmPrimitiveTypeReferenceOrNull(Type clrType) diff --git a/src/Microsoft.OData.ModelBuilder/Commons/TypeHelper.cs b/src/Microsoft.OData.ModelBuilder/Commons/TypeHelper.cs index 9bd4372..d8c62ea 100644 --- a/src/Microsoft.OData.ModelBuilder/Commons/TypeHelper.cs +++ b/src/Microsoft.OData.ModelBuilder/Commons/TypeHelper.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; +using Microsoft.OData.ModelBuilder.Providers; namespace Microsoft.OData.ModelBuilder { diff --git a/src/Microsoft.OData.ModelBuilder/Conventions/Attributes/AttributeEdmPropertyConvention.cs b/src/Microsoft.OData.ModelBuilder/Conventions/Attributes/AttributeEdmPropertyConvention.cs index 7d00a48..81b330a 100644 --- a/src/Microsoft.OData.ModelBuilder/Conventions/Attributes/AttributeEdmPropertyConvention.cs +++ b/src/Microsoft.OData.ModelBuilder/Conventions/Attributes/AttributeEdmPropertyConvention.cs @@ -9,7 +9,7 @@ namespace Microsoft.OData.ModelBuilder.Conventions.Attributes /// Base class for all attribute based 's. /// /// The type of the property this configuration applies to. - internal abstract class AttributeEdmPropertyConvention : AttributeConvention, IEdmPropertyConvention + public abstract class AttributeEdmPropertyConvention : AttributeConvention, IEdmPropertyConvention where TPropertyConfiguration : PropertyConfiguration { /// diff --git a/src/Microsoft.OData.ModelBuilder/Conventions/Attributes/AttributeEdmTypeConvention.cs b/src/Microsoft.OData.ModelBuilder/Conventions/Attributes/AttributeEdmTypeConvention.cs index 474ea12..b743336 100644 --- a/src/Microsoft.OData.ModelBuilder/Conventions/Attributes/AttributeEdmTypeConvention.cs +++ b/src/Microsoft.OData.ModelBuilder/Conventions/Attributes/AttributeEdmTypeConvention.cs @@ -9,7 +9,7 @@ namespace Microsoft.OData.ModelBuilder.Conventions.Attributes /// Base class for all 's based on a attribute on the type. /// /// The kind of Edm type that this convention must be applied to. - internal abstract class AttributeEdmTypeConvention : AttributeConvention, IEdmTypeConvention + public abstract class AttributeEdmTypeConvention : AttributeConvention, IEdmTypeConvention where TEdmTypeConfiguration : class, IEdmTypeConfiguration { /// diff --git a/src/Microsoft.OData.ModelBuilder/Conventions/EntityKeyConvention.cs b/src/Microsoft.OData.ModelBuilder/Conventions/EntityKeyConvention.cs index 58b5e2e..549690f 100644 --- a/src/Microsoft.OData.ModelBuilder/Conventions/EntityKeyConvention.cs +++ b/src/Microsoft.OData.ModelBuilder/Conventions/EntityKeyConvention.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.OData.ModelBuilder.Providers; namespace Microsoft.OData.ModelBuilder { diff --git a/src/Microsoft.OData.ModelBuilder/Helpers/DefaultAssemblyResolver.cs b/src/Microsoft.OData.ModelBuilder/Helpers/DefaultAssemblyResolver.cs index 91cd243..0ef6d84 100644 --- a/src/Microsoft.OData.ModelBuilder/Helpers/DefaultAssemblyResolver.cs +++ b/src/Microsoft.OData.ModelBuilder/Helpers/DefaultAssemblyResolver.cs @@ -12,7 +12,7 @@ namespace Microsoft.OData.ModelBuilder /// public class DefaultAssemblyResolver : IAssemblyResolver { - private Assembly[] _assemblies = GetAssembliesInteral(); + private Assembly[] _assemblies = GetAssembliesInternal(); /// /// This static instance is used in the shared code in places where the request container context @@ -25,7 +25,7 @@ public class DefaultAssemblyResolver : IAssemblyResolver /// public IEnumerable Assemblies => _assemblies; - private static Assembly[] GetAssembliesInteral() + private static Assembly[] GetAssembliesInternal() { return AppDomain.CurrentDomain.GetAssemblies(); } diff --git a/src/Microsoft.OData.ModelBuilder/Helpers/EdmModelHelperMethods.cs b/src/Microsoft.OData.ModelBuilder/Helpers/EdmModelHelperMethods.cs index 7ab8af6..0c639d8 100644 --- a/src/Microsoft.OData.ModelBuilder/Helpers/EdmModelHelperMethods.cs +++ b/src/Microsoft.OData.ModelBuilder/Helpers/EdmModelHelperMethods.cs @@ -13,6 +13,7 @@ using Microsoft.OData.Edm.Vocabularies; using Microsoft.OData.ModelBuilder.Annotations; using Microsoft.OData.ModelBuilder.Config; +using Microsoft.OData.ModelBuilder.Providers; using Microsoft.OData.ModelBuilder.Vocabularies; namespace Microsoft.OData.ModelBuilder.Helpers diff --git a/src/Microsoft.OData.ModelBuilder/Helpers/EdmTypeBuilder.cs b/src/Microsoft.OData.ModelBuilder/Helpers/EdmTypeBuilder.cs index c01b314..18b8bbb 100644 --- a/src/Microsoft.OData.ModelBuilder/Helpers/EdmTypeBuilder.cs +++ b/src/Microsoft.OData.ModelBuilder/Helpers/EdmTypeBuilder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; @@ -354,7 +353,7 @@ private void CreateStructuralTypeBody(EdmStructuredType type, StructuralTypeConf case PropertyKind.Primitive: PrimitivePropertyConfiguration primitiveProperty = (PrimitivePropertyConfiguration)property; EdmPrimitiveTypeKind typeKind = primitiveProperty.TargetEdmTypeKind ?? - GetTypeKind(primitiveProperty.PropertyInfo.PropertyType); + GetTypeKind(primitiveProperty.PropertyInfo.PropertyType); IEdmTypeReference primitiveTypeReference = EdmCoreModel.Instance.GetPrimitive( typeKind, primitiveProperty.NullableProperty); @@ -443,46 +442,60 @@ private void CreateStructuralTypeBody(EdmStructuredType type, StructuralTypeConf private IEdmProperty CreateStructuralTypeCollectionPropertyBody(EdmStructuredType type, CollectionPropertyConfiguration collectionProperty) { - IEdmTypeReference elementTypeReference; - Type clrType = TypeHelper.GetUnderlyingTypeOrSelf(collectionProperty.ElementType); + IEdmTypeReference elementTypeReference= GetElementTypeReference(this, collectionProperty); - if (clrType == typeof(object)) - { - elementTypeReference = EdmCoreModel.Instance.GetUntyped(); - } - else if (TypeHelper.IsEnum(clrType)) + return type.AddStructuralProperty( + collectionProperty.Name, + new EdmCollectionTypeReference(new EdmCollectionType(elementTypeReference))); + + static IEdmTypeReference GetElementTypeReference(EdmTypeBuilder thisParam, CollectionPropertyConfiguration colProperty) { - IEdmType edmType = GetEdmType(clrType); + Type clrType = TypeHelper.GetUnderlyingTypeOrSelf(colProperty.ElementType); - if (edmType == null) + // 1) Untyped + if (clrType == typeof(object)) { - throw Error.InvalidOperation(SRResources.EnumTypeDoesNotExist, clrType.Name); + return EdmCoreModel.Instance.GetUntyped(); } - IEdmEnumType enumElementType = (IEdmEnumType)edmType; - bool isNullable = collectionProperty.ElementType != clrType; - elementTypeReference = new EdmEnumTypeReference(enumElementType, isNullable); - } - else - { - IEdmType edmType = GetEdmType(collectionProperty.ElementType); + // 2) Enum + if (TypeHelper.IsEnum(clrType)) + { + IEdmType enumEdmType = thisParam.GetEdmType(clrType); + + if (enumEdmType == null) + { + throw Error.InvalidOperation(SRResources.EnumTypeDoesNotExist, clrType.Name); + } + + IEdmEnumType enumElementType = (IEdmEnumType)enumEdmType; + bool isNullable = colProperty.ElementType != clrType; + + return new EdmEnumTypeReference(enumElementType, isNullable); + } + + // 3) Explicit primitive override + if (colProperty.ElementTargetEdmTypeKind != null) + { + return EdmCoreModel.Instance.GetPrimitive(colProperty.ElementTargetEdmTypeKind.Value, colProperty.NullableProperty); + } + + // 4) Complex or primitive inferred from known types + IEdmType edmType = thisParam.GetEdmType(colProperty.ElementType); if (edmType != null) { IEdmComplexType elementType = edmType as IEdmComplexType; Contract.Assert(elementType != null); - elementTypeReference = new EdmComplexTypeReference(elementType, collectionProperty.NullableProperty); - } - else - { - elementTypeReference = - EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(collectionProperty.ElementType); - Contract.Assert(elementTypeReference != null); + + return new EdmComplexTypeReference(elementType, colProperty.NullableProperty); } - } - return type.AddStructuralProperty( - collectionProperty.Name, - new EdmCollectionTypeReference(new EdmCollectionType(elementTypeReference))); + // Fallback: primitive by CLR mapping + IEdmTypeReference primitiveTypeReference = EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(colProperty.ElementType); + Contract.Assert(primitiveTypeReference != null); + + return primitiveTypeReference; + } } private IEdmProperty CreateStructuralTypeEnumPropertyBody(EdmStructuredType type, EnumPropertyConfiguration enumProperty) diff --git a/src/Microsoft.OData.ModelBuilder/Microsoft.OData.ModelBuilder.csproj b/src/Microsoft.OData.ModelBuilder/Microsoft.OData.ModelBuilder.csproj index 6b5f8e8..351a9d2 100644 --- a/src/Microsoft.OData.ModelBuilder/Microsoft.OData.ModelBuilder.csproj +++ b/src/Microsoft.OData.ModelBuilder/Microsoft.OData.ModelBuilder.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 Microsoft.OData.ModelBuilder $(OutputPath)$(AssemblyName).xml README.md @@ -43,7 +43,7 @@ - + diff --git a/src/Microsoft.OData.ModelBuilder/Microsoft.OData.ModelBuilder.xml b/src/Microsoft.OData.ModelBuilder/Microsoft.OData.ModelBuilder.xml index d7f17bb..44bce50 100644 --- a/src/Microsoft.OData.ModelBuilder/Microsoft.OData.ModelBuilder.xml +++ b/src/Microsoft.OData.ModelBuilder/Microsoft.OData.ModelBuilder.xml @@ -3331,6 +3331,11 @@ Returns the type of Elements in the Collection + + + Gets the target Edm type kind of this property. Call the extension methods to set this property. + + Sets the CollectionProperty to nullable. @@ -3341,6 +3346,19 @@ Sets the CollectionProperty to required (i.e. non-nullable). + + + Extension methods for . + + + + + Sets an Edm spatial primitive type kind (e.g., GeographyPoint, GeometryLineString, etc). + + Reference to the calling primitive property configuration. + The spatial kind to set. + Returns itself so that multiple calls can be chained. + Represents the configuration for a complex property of a structural type (an entity type or a complex type). @@ -3633,7 +3651,7 @@ - Extensions method for . + Extension methods for . @@ -3652,6 +3670,14 @@ Reference to the calling primitive property configuration. Returns itself so that multiple calls can be chained. + + + Sets an Edm spatial primitive type kind (e.g., GeographyPoint, GeometryLineString, etc). + + Reference to the calling primitive property configuration. + The spatial kind to set. + Returns itself so that multiple calls can be chained. + Base class for all property configurations. @@ -4071,6 +4097,43 @@ Returns itself so that multiple calls can be chained. + + + Defines a service for mapping between CLR types and OData representations. + Implementations provide bidirectional conversion: from CLR types to Edm primitive types, + and from Edm type references back to CLR types. + + + + + Attempts to resolve a CLR type to its corresponding Edm primitive type. + + The CLR type to map. + + When this method returns true, contains the associated ; + otherwise null. + + + true if the CLR type has a corresponding Edm primitive type; otherwise false. + + + This method currently targets Edm primitive types only. A future evolution could return an + to support non-primitive mappings. + + + + + Attempts to resolve an Edm type reference to its corresponding CLR type. + + The Edm type reference to map. + + When this method returns true, contains the associated CLR ; + otherwise null. + + + true if the Edm type reference has a corresponding CLR type; otherwise false. + + A strongly-typed resource class, for looking up localized strings, etc. @@ -4327,6 +4390,11 @@ Looks up a localized string similar to The type '{0}' must be a primitive type.. + + + Looks up a localized string similar to '{0}' is not a valid spatial primitive type kind and cannot be mapped to property '{1}' on type '{2}'.. + + Looks up a localized string similar to The property '{0}' on type '{1}' must be a System.TimeSpan property.. diff --git a/src/Microsoft.OData.ModelBuilder/ODataConventionModelBuilder.cs b/src/Microsoft.OData.ModelBuilder/ODataConventionModelBuilder.cs index 604343b..c7c7382 100644 --- a/src/Microsoft.OData.ModelBuilder/ODataConventionModelBuilder.cs +++ b/src/Microsoft.OData.ModelBuilder/ODataConventionModelBuilder.cs @@ -10,6 +10,7 @@ using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder.Conventions; using Microsoft.OData.ModelBuilder.Conventions.Attributes; +using Microsoft.OData.ModelBuilder.Providers; namespace Microsoft.OData.ModelBuilder { @@ -133,6 +134,8 @@ public ODataConventionModelBuilder(IAssemblyResolver resolver, bool isQueryCompo /// Use this action to modify the configuration that has been inferred by convention. public Action OnModelCreating { get; set; } + internal List EdmTypeMappingProviders { get; } = new List(); + internal void Initialize(IAssemblyResolver assembliesResolver, bool isQueryCompositionMode) { _isQueryCompositionMode = isQueryCompositionMode; @@ -140,11 +143,30 @@ internal void Initialize(IAssemblyResolver assembliesResolver, bool isQueryCompo _mappedTypes = new HashSet(); _ignoredTypes = new HashSet(); ModelAliasingEnabled = true; + EdmLibHelpers.ConfigurePrimitiveTypeMappingProvider(() => + { + // TODO: If DefaultEdmTypeMappingProvider is public, we'd need to modify logic so we don't add it multiple times + // If CompositeEdmTypeMappingProvider is public, we should check and remove it if it's already added + // Or make change in the AddEdmTypeMappingProvider method to not add either to the list + List edmTypeMappingProviders = + [ + .. this.EdmTypeMappingProviders, + new DefaultEdmTypeMappingProvider(), + ]; + + return new CompositeEdmTypeMappingProvider(edmTypeMappingProviders); + }); _allTypesWithDerivedTypeMapping = new Lazy>( () => BuildDerivedTypesMapping(assembliesResolver), isThreadSafe: false); } + internal static IEdmPrimitiveTypeMappingProvider DiscoverProviders( + Func factory, IAssemblyResolver assemblyResolver) + { + return factory(assemblyResolver); + } + /// /// Excludes a type from the model. This is used to remove types from the model that were added by convention during initial model discovery. /// @@ -1161,5 +1183,17 @@ public override void ValidateModel(IEdmModel model) base.ValidateModel(model); } } + + internal void AddConvention(IODataModelConvention convention) + { + if (convention == null) + { + throw Error.ArgumentNull("convention"); + } + + // TODO: Where in the list should we insert the convention? + // Check for duplicates + _conventions.Add(convention); + } } } diff --git a/src/Microsoft.OData.ModelBuilder/ODataConventionModelBuilderExtensions.cs b/src/Microsoft.OData.ModelBuilder/ODataConventionModelBuilderExtensions.cs index 5b4443e..f9e6cb1 100644 --- a/src/Microsoft.OData.ModelBuilder/ODataConventionModelBuilderExtensions.cs +++ b/src/Microsoft.OData.ModelBuilder/ODataConventionModelBuilderExtensions.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using Microsoft.OData.ModelBuilder.Providers; + namespace Microsoft.OData.ModelBuilder { /// @@ -72,5 +74,44 @@ public static ODataConventionModelBuilder EnableLowerCamelCase( builder.OnModelCreating += new LowerCamelCaser(options).ApplyLowerCamelCase; return builder; } + + public static ODataConventionModelBuilder AddEdmTypeMappingProvider( + this ODataConventionModelBuilder builder, + IEdmTypeMappingProvider edmTypeMappingProvider) + { + if (builder == null) + { + throw Error.ArgumentNull("builder"); + } + + if (edmTypeMappingProvider == null) + { + throw Error.ArgumentNull("edmTypeMappingProvider"); + } + + builder.EdmTypeMappingProviders.Add(edmTypeMappingProvider); + + return builder; + } + + public static ODataConventionModelBuilder AddModelConventions( + this ODataConventionModelBuilder builder, + params IODataModelConvention[] conventions) + { + if (builder == null) + { + throw Error.ArgumentNull("builder"); + } + if (conventions == null) + { + throw Error.ArgumentNull("conventions"); + } + foreach (IODataModelConvention convention in conventions) + { + builder.AddConvention(convention); + } + + return builder; + } } } diff --git a/src/Microsoft.OData.ModelBuilder/ODataModelBuilder.cs b/src/Microsoft.OData.ModelBuilder/ODataModelBuilder.cs index 1d54f8e..db84dc7 100644 --- a/src/Microsoft.OData.ModelBuilder/ODataModelBuilder.cs +++ b/src/Microsoft.OData.ModelBuilder/ODataModelBuilder.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder.Helpers; +using Microsoft.OData.ModelBuilder.Providers; namespace Microsoft.OData.ModelBuilder { diff --git a/src/Microsoft.OData.ModelBuilder/Property/CollectionPropertyConfiguration.cs b/src/Microsoft.OData.ModelBuilder/Property/CollectionPropertyConfiguration.cs index cfd6c02..0da11d0 100644 --- a/src/Microsoft.OData.ModelBuilder/Property/CollectionPropertyConfiguration.cs +++ b/src/Microsoft.OData.ModelBuilder/Property/CollectionPropertyConfiguration.cs @@ -3,6 +3,7 @@ using System; using System.Reflection; +using Microsoft.OData.Edm; namespace Microsoft.OData.ModelBuilder { @@ -46,6 +47,11 @@ public Type ElementType get { return _elementType; } } + /// + /// Gets the target Edm type kind of this property. Call the extension methods to set this property. + /// + public EdmPrimitiveTypeKind? ElementTargetEdmTypeKind { get; internal set; } + /// /// Sets the CollectionProperty to nullable. /// diff --git a/src/Microsoft.OData.ModelBuilder/Property/CollectionPropertyConfigurationExtensions.cs b/src/Microsoft.OData.ModelBuilder/Property/CollectionPropertyConfigurationExtensions.cs new file mode 100644 index 0000000..5e54d4f --- /dev/null +++ b/src/Microsoft.OData.ModelBuilder/Property/CollectionPropertyConfigurationExtensions.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.OData.Edm; + +namespace Microsoft.OData.ModelBuilder +{ + /// + /// Extension methods for . + /// + public static class CollectionPropertyConfigurationExtensions + { + /// + /// Sets an Edm spatial primitive type kind (e.g., GeographyPoint, GeometryLineString, etc). + /// + /// Reference to the calling primitive property configuration. + /// The spatial kind to set. + /// Returns itself so that multiple calls can be chained. + public static CollectionPropertyConfiguration AsSpatial(this CollectionPropertyConfiguration property, EdmPrimitiveTypeKind spatialKind) + { + if (property == null) + { + throw Error.ArgumentNull("property"); + } + + if (!IsSpatialKind(spatialKind)) + { + throw Error.Argument("spatialKind", + SRResources.MustBeSpatialEdmTypeKind, + spatialKind.ToString(), + property.PropertyInfo.Name, + property.DeclaringType.FullName); + } + + property.ElementTargetEdmTypeKind = spatialKind; + return property; + } + + internal static bool IsSpatialKind(EdmPrimitiveTypeKind kind) + { + switch (kind) + { + case EdmPrimitiveTypeKind.Geometry: + case EdmPrimitiveTypeKind.GeometryPoint: + case EdmPrimitiveTypeKind.GeometryLineString: + case EdmPrimitiveTypeKind.GeometryPolygon: + case EdmPrimitiveTypeKind.GeometryMultiPoint: + case EdmPrimitiveTypeKind.GeometryMultiLineString: + case EdmPrimitiveTypeKind.GeometryMultiPolygon: + case EdmPrimitiveTypeKind.GeometryCollection: + case EdmPrimitiveTypeKind.Geography: + case EdmPrimitiveTypeKind.GeographyPoint: + case EdmPrimitiveTypeKind.GeographyLineString: + case EdmPrimitiveTypeKind.GeographyPolygon: + case EdmPrimitiveTypeKind.GeographyMultiPoint: + case EdmPrimitiveTypeKind.GeographyMultiLineString: + case EdmPrimitiveTypeKind.GeographyMultiPolygon: + case EdmPrimitiveTypeKind.GeographyCollection: + return true; + default: + return false; + } + } + } +} diff --git a/src/Microsoft.OData.ModelBuilder/Property/NavigationPropertyConfiguration.cs b/src/Microsoft.OData.ModelBuilder/Property/NavigationPropertyConfiguration.cs index 0e864de..1238b4e 100644 --- a/src/Microsoft.OData.ModelBuilder/Property/NavigationPropertyConfiguration.cs +++ b/src/Microsoft.OData.ModelBuilder/Property/NavigationPropertyConfiguration.cs @@ -7,6 +7,7 @@ using Microsoft.OData.Edm; using System.Diagnostics.Contracts; using System.Linq; +using Microsoft.OData.ModelBuilder.Providers; namespace Microsoft.OData.ModelBuilder { diff --git a/src/Microsoft.OData.ModelBuilder/Property/PrimitivePropertyConfigurationExtensions.cs b/src/Microsoft.OData.ModelBuilder/Property/PrimitivePropertyConfigurationExtensions.cs index 4e85ade..4bfeca3 100644 --- a/src/Microsoft.OData.ModelBuilder/Property/PrimitivePropertyConfigurationExtensions.cs +++ b/src/Microsoft.OData.ModelBuilder/Property/PrimitivePropertyConfigurationExtensions.cs @@ -6,7 +6,7 @@ namespace Microsoft.OData.ModelBuilder { /// - /// Extensions method for . + /// Extension methods for . /// public static class PrimitivePropertyConfigurationExtensions { @@ -55,5 +55,57 @@ public static PrimitivePropertyConfiguration AsTimeOfDay(this PrimitivePropertyC property.TargetEdmTypeKind = EdmPrimitiveTypeKind.TimeOfDay; return property; } + + /// + /// Sets an Edm spatial primitive type kind (e.g., GeographyPoint, GeometryLineString, etc). + /// + /// Reference to the calling primitive property configuration. + /// The spatial kind to set. + /// Returns itself so that multiple calls can be chained. + public static PrimitivePropertyConfiguration AsSpatial(this PrimitivePropertyConfiguration property, EdmPrimitiveTypeKind spatialKind) + { + if (property == null) + { + throw Error.ArgumentNull("property"); + } + + if (!IsSpatialKind(spatialKind)) + { + throw Error.Argument("spatialKind", + SRResources.MustBeSpatialEdmTypeKind, + spatialKind.ToString(), + property.PropertyInfo.Name, + property.DeclaringType.FullName); + } + + property.TargetEdmTypeKind = spatialKind; + return property; + } + + private static bool IsSpatialKind(EdmPrimitiveTypeKind kind) + { + switch (kind) + { + case EdmPrimitiveTypeKind.Geometry: + case EdmPrimitiveTypeKind.GeometryPoint: + case EdmPrimitiveTypeKind.GeometryLineString: + case EdmPrimitiveTypeKind.GeometryPolygon: + case EdmPrimitiveTypeKind.GeometryMultiPoint: + case EdmPrimitiveTypeKind.GeometryMultiLineString: + case EdmPrimitiveTypeKind.GeometryMultiPolygon: + case EdmPrimitiveTypeKind.GeometryCollection: + case EdmPrimitiveTypeKind.Geography: + case EdmPrimitiveTypeKind.GeographyPoint: + case EdmPrimitiveTypeKind.GeographyLineString: + case EdmPrimitiveTypeKind.GeographyPolygon: + case EdmPrimitiveTypeKind.GeographyMultiPoint: + case EdmPrimitiveTypeKind.GeographyMultiLineString: + case EdmPrimitiveTypeKind.GeographyMultiPolygon: + case EdmPrimitiveTypeKind.GeographyCollection: + return true; + default: + return false; + } + } } } diff --git a/src/Microsoft.OData.ModelBuilder/Providers/CompositeEdmTypeMappingProvider.cs b/src/Microsoft.OData.ModelBuilder/Providers/CompositeEdmTypeMappingProvider.cs new file mode 100644 index 0000000..bc4c7a6 --- /dev/null +++ b/src/Microsoft.OData.ModelBuilder/Providers/CompositeEdmTypeMappingProvider.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.OData.Edm; + +namespace Microsoft.OData.ModelBuilder.Providers +{ + public sealed class CompositeEdmTypeMappingProvider : IEdmTypeMappingProvider + { + private readonly IEnumerable _providers; + + public CompositeEdmTypeMappingProvider(IEnumerable providers) + { + if (providers == null || providers.Count() == 0) + { + // TODO: Use resource manager for error messages + throw new ArgumentException("At least one provider must be specified.", nameof(providers)); + } + + _providers = providers; + } + + // TODO: Consider making this public if needed + internal IEnumerable Providers => _providers; + + public bool TryGetEdmType(Type clrType, out IEdmPrimitiveType primitiveType) + { + foreach (IEdmTypeMappingProvider provider in _providers) + { + if (provider.TryGetEdmType(clrType, out primitiveType)) + { + return true; + } + } + + primitiveType = null; + return false; + } + + public bool TryGetClrType(IEdmTypeReference edmTypeReference, out Type clrType) + { + foreach (IEdmTypeMappingProvider provider in _providers) + { + if (provider.TryGetClrType(edmTypeReference, out clrType)) + { + return true; + } + } + + clrType = null; + return false; + } + } +} diff --git a/src/Microsoft.OData.ModelBuilder/Providers/DefaultEdmTypeMappingProvider.cs b/src/Microsoft.OData.ModelBuilder/Providers/DefaultEdmTypeMappingProvider.cs new file mode 100644 index 0000000..50b212e --- /dev/null +++ b/src/Microsoft.OData.ModelBuilder/Providers/DefaultEdmTypeMappingProvider.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Microsoft.OData.Edm; +using Microsoft.Spatial; + +namespace Microsoft.OData.ModelBuilder.Providers +{ + public class DefaultEdmTypeMappingProvider : IEdmTypeMappingProvider + { + private static readonly EdmCoreModel _coreModel = EdmCoreModel.Instance; + + private static readonly Dictionary _builtInTypesMapping = + new[] + { + new KeyValuePair(typeof(string), GetPrimitiveType(EdmPrimitiveTypeKind.String)), + new KeyValuePair(typeof(bool), GetPrimitiveType(EdmPrimitiveTypeKind.Boolean)), + new KeyValuePair(typeof(bool?), GetPrimitiveType(EdmPrimitiveTypeKind.Boolean)), + new KeyValuePair(typeof(byte), GetPrimitiveType(EdmPrimitiveTypeKind.Byte)), + new KeyValuePair(typeof(byte?), GetPrimitiveType(EdmPrimitiveTypeKind.Byte)), + new KeyValuePair(typeof(decimal), GetPrimitiveType(EdmPrimitiveTypeKind.Decimal)), + new KeyValuePair(typeof(decimal?), GetPrimitiveType(EdmPrimitiveTypeKind.Decimal)), + new KeyValuePair(typeof(double), GetPrimitiveType(EdmPrimitiveTypeKind.Double)), + new KeyValuePair(typeof(double?), GetPrimitiveType(EdmPrimitiveTypeKind.Double)), + new KeyValuePair(typeof(Guid), GetPrimitiveType(EdmPrimitiveTypeKind.Guid)), + new KeyValuePair(typeof(Guid?), GetPrimitiveType(EdmPrimitiveTypeKind.Guid)), + new KeyValuePair(typeof(short), GetPrimitiveType(EdmPrimitiveTypeKind.Int16)), + new KeyValuePair(typeof(short?), GetPrimitiveType(EdmPrimitiveTypeKind.Int16)), + new KeyValuePair(typeof(int), GetPrimitiveType(EdmPrimitiveTypeKind.Int32)), + new KeyValuePair(typeof(int?), GetPrimitiveType(EdmPrimitiveTypeKind.Int32)), + new KeyValuePair(typeof(long), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), + new KeyValuePair(typeof(long?), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), + new KeyValuePair(typeof(sbyte), GetPrimitiveType(EdmPrimitiveTypeKind.SByte)), + new KeyValuePair(typeof(sbyte?), GetPrimitiveType(EdmPrimitiveTypeKind.SByte)), + new KeyValuePair(typeof(float), GetPrimitiveType(EdmPrimitiveTypeKind.Single)), + new KeyValuePair(typeof(float?), GetPrimitiveType(EdmPrimitiveTypeKind.Single)), + new KeyValuePair(typeof(byte[]), GetPrimitiveType(EdmPrimitiveTypeKind.Binary)), + new KeyValuePair(typeof(Stream), GetPrimitiveType(EdmPrimitiveTypeKind.Stream)), + new KeyValuePair(typeof(Geography), GetPrimitiveType(EdmPrimitiveTypeKind.Geography)), + new KeyValuePair(typeof(GeographyPoint), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyPoint)), + new KeyValuePair(typeof(GeographyLineString), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyLineString)), + new KeyValuePair(typeof(GeographyPolygon), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyPolygon)), + new KeyValuePair(typeof(GeographyCollection), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyCollection)), + new KeyValuePair(typeof(GeographyMultiLineString), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyMultiLineString)), + new KeyValuePair(typeof(GeographyMultiPoint), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyMultiPoint)), + new KeyValuePair(typeof(GeographyMultiPolygon), GetPrimitiveType(EdmPrimitiveTypeKind.GeographyMultiPolygon)), + new KeyValuePair(typeof(Geometry), GetPrimitiveType(EdmPrimitiveTypeKind.Geometry)), + new KeyValuePair(typeof(GeometryPoint), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryPoint)), + new KeyValuePair(typeof(GeometryLineString), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryLineString)), + new KeyValuePair(typeof(GeometryPolygon), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryPolygon)), + new KeyValuePair(typeof(GeometryCollection), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryCollection)), + new KeyValuePair(typeof(GeometryMultiLineString), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryMultiLineString)), + new KeyValuePair(typeof(GeometryMultiPoint), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryMultiPoint)), + new KeyValuePair(typeof(GeometryMultiPolygon), GetPrimitiveType(EdmPrimitiveTypeKind.GeometryMultiPolygon)), + new KeyValuePair(typeof(DateTimeOffset), GetPrimitiveType(EdmPrimitiveTypeKind.DateTimeOffset)), + new KeyValuePair(typeof(DateTimeOffset?), GetPrimitiveType(EdmPrimitiveTypeKind.DateTimeOffset)), + new KeyValuePair(typeof(TimeSpan), GetPrimitiveType(EdmPrimitiveTypeKind.Duration)), + new KeyValuePair(typeof(TimeSpan?), GetPrimitiveType(EdmPrimitiveTypeKind.Duration)), + new KeyValuePair(typeof(Date), GetPrimitiveType(EdmPrimitiveTypeKind.Date)), + new KeyValuePair(typeof(Date?), GetPrimitiveType(EdmPrimitiveTypeKind.Date)), + new KeyValuePair(typeof(TimeOfDay), GetPrimitiveType(EdmPrimitiveTypeKind.TimeOfDay)), + new KeyValuePair(typeof(TimeOfDay?), GetPrimitiveType(EdmPrimitiveTypeKind.TimeOfDay)), + new KeyValuePair(typeof(DateOnly), GetPrimitiveType(EdmPrimitiveTypeKind.Date)), + new KeyValuePair(typeof(DateOnly?), GetPrimitiveType(EdmPrimitiveTypeKind.Date)), + new KeyValuePair(typeof(TimeOnly), GetPrimitiveType(EdmPrimitiveTypeKind.TimeOfDay)), + new KeyValuePair(typeof(TimeOnly?), GetPrimitiveType(EdmPrimitiveTypeKind.TimeOfDay)), + + // Keep the Binary and XElement in the end, since there are not the default mappings for Edm.Binary and Edm.String. + new KeyValuePair(typeof(XElement), GetPrimitiveType(EdmPrimitiveTypeKind.String)), + // new KeyValuePair(typeof(Binary), GetPrimitiveType(EdmPrimitiveTypeKind.Binary)), + new KeyValuePair(typeof(ushort), GetPrimitiveType(EdmPrimitiveTypeKind.Int32)), + new KeyValuePair(typeof(ushort?), GetPrimitiveType(EdmPrimitiveTypeKind.Int32)), + new KeyValuePair(typeof(uint), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), + new KeyValuePair(typeof(uint?), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), + new KeyValuePair(typeof(ulong), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), + new KeyValuePair(typeof(ulong?), GetPrimitiveType(EdmPrimitiveTypeKind.Int64)), + new KeyValuePair(typeof(char[]), GetPrimitiveType(EdmPrimitiveTypeKind.String)), + new KeyValuePair(typeof(char), GetPrimitiveType(EdmPrimitiveTypeKind.String)), + new KeyValuePair(typeof(char?), GetPrimitiveType(EdmPrimitiveTypeKind.String)), + new KeyValuePair(typeof(DateTime), GetPrimitiveType(EdmPrimitiveTypeKind.DateTimeOffset)), + new KeyValuePair(typeof(DateTime?), GetPrimitiveType(EdmPrimitiveTypeKind.DateTimeOffset)), + } + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + public virtual bool TryGetEdmType(Type clrType, out IEdmPrimitiveType primitiveType) + { + return _builtInTypesMapping.TryGetValue(clrType, out primitiveType); + } + + public virtual bool TryGetClrType(IEdmTypeReference edmTypeReference, out Type clrType) + { + clrType = null; + + if (edmTypeReference == null || !edmTypeReference.IsPrimitive()) + { + return false; + } + + foreach (KeyValuePair kvp in _builtInTypesMapping) + { + if (edmTypeReference.Definition.IsEquivalentTo(kvp.Value) + && (!edmTypeReference.IsNullable || EdmLibHelpers.IsNullable(kvp.Key))) + { + clrType = kvp.Key; + return true; + } + } + + return false; + } + + private static IEdmPrimitiveType GetPrimitiveType(EdmPrimitiveTypeKind primitiveKind) + { + return _coreModel.GetPrimitiveType(primitiveKind); + } + } +} diff --git a/src/Microsoft.OData.ModelBuilder/Providers/IEdmTypeMappingProvider.cs b/src/Microsoft.OData.ModelBuilder/Providers/IEdmTypeMappingProvider.cs new file mode 100644 index 0000000..4b5a9db --- /dev/null +++ b/src/Microsoft.OData.ModelBuilder/Providers/IEdmTypeMappingProvider.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using Microsoft.OData.Edm; + +namespace Microsoft.OData.ModelBuilder.Providers +{ + /// + /// Defines a service for mapping between CLR types and OData representations. + /// Implementations provide bidirectional conversion: from CLR types to Edm primitive types, + /// and from Edm type references back to CLR types. + /// + public interface IEdmTypeMappingProvider + { + /// + /// Attempts to resolve a CLR type to its corresponding Edm primitive type. + /// + /// The CLR type to map. + /// + /// When this method returns true, contains the associated ; + /// otherwise null. + /// + /// + /// true if the CLR type has a corresponding Edm primitive type; otherwise false. + /// + /// + /// This method currently targets Edm primitive types only. A future evolution could return an + /// to support non-primitive mappings. + /// + bool TryGetEdmType(Type clrType, out IEdmPrimitiveType primitiveType); + + /// + /// Attempts to resolve an Edm type reference to its corresponding CLR type. + /// + /// The Edm type reference to map. + /// + /// When this method returns true, contains the associated CLR ; + /// otherwise null. + /// + /// + /// true if the Edm type reference has a corresponding CLR type; otherwise false. + /// + bool TryGetClrType(IEdmTypeReference edmTypeReference, out Type clrType); + } +} \ No newline at end of file diff --git a/src/Microsoft.OData.ModelBuilder/SRResources.Designer.cs b/src/Microsoft.OData.ModelBuilder/SRResources.Designer.cs index 3188d67..1eac6ff 100644 --- a/src/Microsoft.OData.ModelBuilder/SRResources.Designer.cs +++ b/src/Microsoft.OData.ModelBuilder/SRResources.Designer.cs @@ -492,6 +492,15 @@ internal static string MustBePrimitiveType { } } + /// + /// Looks up a localized string similar to '{0}' is not a valid spatial primitive type kind and cannot be mapped to property '{1}' on type '{2}'.. + /// + internal static string MustBeSpatialEdmTypeKind { + get { + return ResourceManager.GetString("MustBeSpatialEdmTypeKind", resourceCulture); + } + } + /// /// Looks up a localized string similar to The property '{0}' on type '{1}' must be a System.TimeSpan property.. /// diff --git a/src/Microsoft.OData.ModelBuilder/SRResources.resx b/src/Microsoft.OData.ModelBuilder/SRResources.resx index 1328077..32e15af 100644 --- a/src/Microsoft.OData.ModelBuilder/SRResources.resx +++ b/src/Microsoft.OData.ModelBuilder/SRResources.resx @@ -342,4 +342,7 @@ The input instance annotation name '{0}' is not invalid. An instance annotation should be simple identifier with '.' in it. + + '{0}' is not a valid spatial primitive type kind and cannot be mapped to property '{1}' on type '{2}'. + \ No newline at end of file diff --git a/src/Microsoft.OData.ModelBuilder/Types/StructuralTypeConfiguration.cs b/src/Microsoft.OData.ModelBuilder/Types/StructuralTypeConfiguration.cs index f87136b..498b67d 100644 --- a/src/Microsoft.OData.ModelBuilder/Types/StructuralTypeConfiguration.cs +++ b/src/Microsoft.OData.ModelBuilder/Types/StructuralTypeConfiguration.cs @@ -9,6 +9,7 @@ using System.Reflection; using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder.Config; +using Microsoft.OData.ModelBuilder.Providers; using Microsoft.OData.ModelBuilder.Vocabularies; namespace Microsoft.OData.ModelBuilder