From e507855d81f56637ac241728e35b7348544aa306 Mon Sep 17 00:00:00 2001 From: Maxim Voronov Date: Wed, 26 Feb 2020 20:18:51 +0300 Subject: [PATCH] json schema add readonly datatype property, fix #40 --- .../OeEf6EdmModelMetadataProvider.cs | 18 +++++++++ .../OeEfCoreEdmModelMetadataProvider.cs | 11 ++++++ .../OeLinq2DbEdmModelMetadataProvider.cs | 4 ++ .../ModelBuilder/EntityTypeInfo.cs | 37 ++++++++++++------- .../ModelBuilder/OeEdmModelBuilder.cs | 4 +- .../OeEdmModelMetadataProvider.cs | 5 +++ .../ModelBuilder/OeJsonSchemaGenerator.cs | 15 ++++++-- .../EdmModelBuilderTest.cs | 5 +++ .../EdmModelBuilderTest.cs | 3 +- 9 files changed, 83 insertions(+), 19 deletions(-) diff --git a/source/OdataToEntity.Ef6/OeEf6EdmModelMetadataProvider.cs b/source/OdataToEntity.Ef6/OeEf6EdmModelMetadataProvider.cs index 72e0d0f..0e7f429 100644 --- a/source/OdataToEntity.Ef6/OeEf6EdmModelMetadataProvider.cs +++ b/source/OdataToEntity.Ef6/OeEf6EdmModelMetadataProvider.cs @@ -109,6 +109,24 @@ public override PropertyInfo[] GetPrincipalToDependentWithoutDependent(PropertyI return null; } + private static StoreGeneratedPattern GetStoreGeneratedPattern(EdmProperty property) + { + if (property.MetadataProperties.TryGetValue("http://schemas.microsoft.com/ado/2009/02/edm/annotation:StoreGeneratedPattern", false, out MetadataProperty item)) + return (StoreGeneratedPattern)Enum.Parse(typeof(StoreGeneratedPattern), (String)item.Value); + return StoreGeneratedPattern.None; + } + public override bool IsDatabaseGenerated(PropertyInfo propertyInfo) + { + foreach (EntityType efEntityType in GetEntityTypes(propertyInfo)) + { + EdmProperty efProperty = efEntityType.DeclaredProperties.FirstOrDefault(p => p.Name == propertyInfo.Name); + if (efProperty != null) + return GetStoreGeneratedPattern(efProperty) != StoreGeneratedPattern.None; + } + + throw new InvalidOperationException("property " + propertyInfo.Name + " not found"); + + } public override bool IsKey(PropertyInfo propertyInfo) { foreach (EntityType efEntityType in GetEntityTypes(propertyInfo)) diff --git a/source/OdataToEntity.EfCore/OeEfCoreEdmModelMetadataProvider.cs b/source/OdataToEntity.EfCore/OeEfCoreEdmModelMetadataProvider.cs index c036031..482aeb2 100644 --- a/source/OdataToEntity.EfCore/OeEfCoreEdmModelMetadataProvider.cs +++ b/source/OdataToEntity.EfCore/OeEfCoreEdmModelMetadataProvider.cs @@ -162,6 +162,17 @@ private PropertyInfo GetPropertyInfo(IPropertyBase efProperty) return efProperty.PropertyInfo; } + public override bool IsDatabaseGenerated(PropertyInfo propertyInfo) + { + foreach (IEntityType efEntityType in GetEntityTypes(propertyInfo)) + { + IProperty efProperty = efEntityType.FindProperty(propertyInfo.Name); + if (efProperty != null) + return efProperty.ValueGenerated != ValueGenerated.Never; + } + + throw new InvalidOperationException("property " + propertyInfo.Name + " not found"); + } public override bool IsKey(PropertyInfo propertyInfo) { foreach (IEntityType efEntityType in GetEntityTypes(propertyInfo)) diff --git a/source/OdataToEntity.Linq2Db/OeLinq2DbEdmModelMetadataProvider.cs b/source/OdataToEntity.Linq2Db/OeLinq2DbEdmModelMetadataProvider.cs index 3434cee..00b9863 100644 --- a/source/OdataToEntity.Linq2Db/OeLinq2DbEdmModelMetadataProvider.cs +++ b/source/OdataToEntity.Linq2Db/OeLinq2DbEdmModelMetadataProvider.cs @@ -86,6 +86,10 @@ public override PropertyInfo[] GetPrincipalToDependentWithoutDependent(PropertyI } throw new InvalidOperationException("Pricipal structurlal property not found for principal navigation " + principalNavigation.Name); } + public override bool IsDatabaseGenerated(PropertyInfo propertyInfo) + { + return propertyInfo.GetCustomAttribute(typeof(IdentityAttribute)) != null; + } public override bool IsKey(PropertyInfo propertyInfo) => propertyInfo.GetCustomAttribute(typeof(PrimaryKeyAttribute)) != null; public override bool IsNotMapped(PropertyInfo propertyInfo) { diff --git a/source/OdataToEntity/ModelBuilder/EntityTypeInfo.cs b/source/OdataToEntity/ModelBuilder/EntityTypeInfo.cs index 4039d20..7879e06 100644 --- a/source/OdataToEntity/ModelBuilder/EntityTypeInfo.cs +++ b/source/OdataToEntity/ModelBuilder/EntityTypeInfo.cs @@ -1,4 +1,6 @@ using Microsoft.OData.Edm; +using Microsoft.OData.Edm.Vocabularies; +using Microsoft.OData.Edm.Vocabularies.V1; using System; using System.Collections.Generic; using System.Linq; @@ -75,7 +77,26 @@ private void AddKeys() } EdmType.AddKeys(keys.OrderBy(p => p.Item2).Select(p => p.Item1)); } - private void BuildProperty(Dictionary entityTypes, + public void BuildStructuralProperties(EdmModel edmModel, Dictionary entityTypes, + Dictionary enumTypes, Dictionary complexTypes) + { + foreach (PropertyInfo clrProperty in _metadataProvider.GetProperties(ClrType)) + if (!_metadataProvider.IsNotMapped(clrProperty)) + { + EdmStructuralProperty? edmProperty = BuildStructuralProperty(entityTypes, enumTypes, complexTypes, clrProperty); + if (edmProperty != null && _metadataProvider.IsDatabaseGenerated(clrProperty)) + { + var databaseGenerated = new EdmVocabularyAnnotation(edmProperty, CoreVocabularyModel.ComputedTerm, new EdmBooleanConstant(true)); + edmModel.SetVocabularyAnnotation(databaseGenerated); + } + } + + if (_isDbQuery) + AddDbQueryKeys(); + else + AddKeys(); + } + private EdmStructuralProperty? BuildStructuralProperty(Dictionary entityTypes, Dictionary enumTypes, Dictionary complexTypes, PropertyInfo clrProperty) { bool isNullable = !_metadataProvider.IsRequired(clrProperty); @@ -103,7 +124,7 @@ private void BuildProperty(Dictionary entityTypes, FKeyInfo? fkeyInfo = FKeyInfo.Create(_metadataProvider, entityTypes, this, clrProperty); if (fkeyInfo != null) _navigationClrProperties.Add(fkeyInfo); - return; + return null; } } } @@ -122,18 +143,8 @@ private void BuildProperty(Dictionary entityTypes, EdmType.AddProperty(edmProperty); if (_metadataProvider.IsKey(clrProperty)) _keyProperties.Add(new KeyValuePair(clrProperty, edmProperty)); - } - public void BuildProperties(Dictionary entityTypes, - Dictionary enumTypes, Dictionary complexTypes) - { - foreach (PropertyInfo clrProperty in _metadataProvider.GetProperties(ClrType)) - if (!_metadataProvider.IsNotMapped(clrProperty)) - BuildProperty(entityTypes, enumTypes, complexTypes, clrProperty); - if (_isDbQuery) - AddDbQueryKeys(); - else - AddKeys(); + return edmProperty; } public override String ToString() { diff --git a/source/OdataToEntity/ModelBuilder/OeEdmModelBuilder.cs b/source/OdataToEntity/ModelBuilder/OeEdmModelBuilder.cs index 21400a8..e50b50c 100644 --- a/source/OdataToEntity/ModelBuilder/OeEdmModelBuilder.cs +++ b/source/OdataToEntity/ModelBuilder/OeEdmModelBuilder.cs @@ -57,18 +57,18 @@ private EdmAction BuildAction(OeOperationConfiguration operationConfiguration, D public EdmModel BuildEdmModel(params IEdmModel[] refModels) { AddOperations(); + var edmModel = new EdmModel(false); Dictionary entityTypeInfos = BuildEntityTypes(refModels); foreach (EntityTypeInfo typeInfo in entityTypeInfos.Values) if (!typeInfo.IsRefModel) - typeInfo.BuildProperties(entityTypeInfos, _enumTypes, _complexTypes); + typeInfo.BuildStructuralProperties(edmModel, entityTypeInfos, _enumTypes, _complexTypes); foreach (EntityTypeInfo typeInfo in entityTypeInfos.Values) if (!typeInfo.IsRefModel) foreach (FKeyInfo fkeyInfo in typeInfo.NavigationClrProperties) fkeyInfo.BuildNavigationProperty(); - var edmModel = new EdmModel(false); edmModel.AddElements(_enumTypes.Values); foreach (KeyValuePair enumType in _enumTypes) edmModel.SetClrType(enumType.Value, enumType.Key); diff --git a/source/OdataToEntity/ModelBuilder/OeEdmModelMetadataProvider.cs b/source/OdataToEntity/ModelBuilder/OeEdmModelMetadataProvider.cs index 38f805d..8a5190c 100644 --- a/source/OdataToEntity/ModelBuilder/OeEdmModelMetadataProvider.cs +++ b/source/OdataToEntity/ModelBuilder/OeEdmModelMetadataProvider.cs @@ -83,6 +83,11 @@ public virtual PropertyInfo[] GetProperties(Type type) { return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); } + public virtual bool IsDatabaseGenerated(PropertyInfo propertyInfo) + { + DatabaseGeneratedAttribute attribute = propertyInfo.GetCustomAttribute(); + return attribute != null && attribute.DatabaseGeneratedOption != DatabaseGeneratedOption.None; + } public virtual bool IsKey(PropertyInfo propertyInfo) { return propertyInfo.GetCustomAttribute(typeof(KeyAttribute)) != null; diff --git a/source/OdataToEntity/ModelBuilder/OeJsonSchemaGenerator.cs b/source/OdataToEntity/ModelBuilder/OeJsonSchemaGenerator.cs index 2024fb0..9df6ec4 100644 --- a/source/OdataToEntity/ModelBuilder/OeJsonSchemaGenerator.cs +++ b/source/OdataToEntity/ModelBuilder/OeJsonSchemaGenerator.cs @@ -1,7 +1,10 @@ using Microsoft.OData.Edm; +using Microsoft.OData.Edm.Vocabularies; +using Microsoft.OData.Edm.Vocabularies.V1; using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.Json; namespace OdataToEntity.ModelBuilder @@ -36,7 +39,7 @@ private void WriteDefinitions(Utf8JsonWriter writer) WriteDefinitions(writer, edmModel); writer.WriteEndObject(); } - public static void WriteDefinitions(Utf8JsonWriter writer, IEdmModel edmModel) + public void WriteDefinitions(Utf8JsonWriter writer, IEdmModel edmModel) { foreach (IEdmSchemaElement schemaElement in edmModel.SchemaElements) if (schemaElement is IEdmEntityType entityType) @@ -47,7 +50,7 @@ public static void WriteDefinitions(Utf8JsonWriter writer, IEdmModel edmModel) else if (schemaElement is IEdmEnumType enumType) WriteEnum(writer, enumType); } - private static void WriteEntity(Utf8JsonWriter writer, IEdmEntityType entityType) + private void WriteEntity(Utf8JsonWriter writer, IEdmEntityType entityType) { writer.WritePropertyName(entityType.Name); writer.WriteStartObject(); @@ -150,7 +153,7 @@ private static void WriteProperties(Utf8JsonWriter writer, IEdmModel edmModel) writer.WriteEndObject(); } } - private static void WriteStructuralProperty(Utf8JsonWriter writer, IEdmStructuralProperty structuralProperty) + private void WriteStructuralProperty(Utf8JsonWriter writer, IEdmStructuralProperty structuralProperty) { if (structuralProperty.Type.Definition is IEdmEnumType enumType) { @@ -221,8 +224,14 @@ private static void WriteStructuralProperty(Utf8JsonWriter writer, IEdmStructura writer.WritePropertyName(structuralProperty.Name); writer.WriteStartObject(); writer.WriteString("type", typeName); + if (format != null) writer.WriteString("format", format); + + IEdmVocabularyAnnotation? annotation = _edmModel.FindVocabularyAnnotations(structuralProperty, CoreVocabularyModel.ComputedTerm).SingleOrDefault(); + if (annotation != null && annotation.Value is IEdmBooleanConstantExpression expression && expression.Value) + writer.WriteBoolean("readOnly", true); + writer.WriteEndObject(); } } diff --git a/test/OdataToEntity.Test.Ef6.SqlServer/EdmModelBuilderTest.cs b/test/OdataToEntity.Test.Ef6.SqlServer/EdmModelBuilderTest.cs index 1dade91..dbeb0e0 100644 --- a/test/OdataToEntity.Test.Ef6.SqlServer/EdmModelBuilderTest.cs +++ b/test/OdataToEntity.Test.Ef6.SqlServer/EdmModelBuilderTest.cs @@ -55,6 +55,11 @@ private static String FixNamesInSchema(String schema) } xschemas[1].Remove(); + var annotations = xschemas[0].Elements().Where(x => x.Name.LocalName == "Annotations").ToList(); + foreach (XElement annotation in annotations) + annotation.Remove(); + xschemas[0].Add(annotations); + using (var stream = new MemoryStream()) using (var xwriter = XmlWriter.Create(stream, new XmlWriterSettings() { Indent = true, Encoding = new UTF8Encoding(false) })) { diff --git a/test/OdataToEntity.Test.Linq2Db/EdmModelBuilderTest.cs b/test/OdataToEntity.Test.Linq2Db/EdmModelBuilderTest.cs index eebe514..cf7f6d6 100644 --- a/test/OdataToEntity.Test.Linq2Db/EdmModelBuilderTest.cs +++ b/test/OdataToEntity.Test.Linq2Db/EdmModelBuilderTest.cs @@ -28,7 +28,8 @@ private static String FixNamesInSchema(String schema) foreach (XElement xelement in xschema.Elements()) { XAttribute xattribute = xelement.Attribute("Name"); - xattribute.SetValue(xattribute.Value.Replace(linq2db.Name, efCore.Name)); + if (xattribute != null) + xattribute.SetValue(xattribute.Value.Replace(linq2db.Name, efCore.Name)); } using (var stream = new MemoryStream())