From ce8756972c5219aa51e6d713fa6b0fb12fd2e6b5 Mon Sep 17 00:00:00 2001 From: volkanceylan Date: Wed, 18 Oct 2023 19:17:24 +0300 Subject: [PATCH] initial try at reusing existing entities while generating code by generating ForeignKey(typeof(SomeRow)) instead of ForeignKey("SomeTable", "SomeId") --- .../Commands/GenerateCommand.cs | 17 ++- .../Generator/EntityModelGenerator.cs | 8 +- .../Models/AttributeTypeRef.cs | 2 +- .../Models/EntityModelInputs.cs | 3 +- .../Models/IEntityModelInputs.cs | 3 +- .../Models/Metadata/ApplicationMetadata.cs | 118 ++++++++++++++++++ .../Models/Metadata/IApplicationMetadata.cs | 6 + .../Models/Metadata/IClassMetadata.cs | 8 ++ .../Models/Metadata/IPropertyMetadata.cs | 6 + .../Models/Metadata/IRowMetadata.cs | 6 + .../Models/Metadata/IRowPropertyMetadata.cs | 5 + .../Models/{TypeNameRef.cs => TypeOfRef.cs} | 4 +- .../Reflection/CodeWriter.cs | 9 +- 13 files changed, 185 insertions(+), 10 deletions(-) create mode 100644 src/Serenity.Net.CodeGenerator/Models/Metadata/ApplicationMetadata.cs create mode 100644 src/Serenity.Net.CodeGenerator/Models/Metadata/IApplicationMetadata.cs create mode 100644 src/Serenity.Net.CodeGenerator/Models/Metadata/IClassMetadata.cs create mode 100644 src/Serenity.Net.CodeGenerator/Models/Metadata/IPropertyMetadata.cs create mode 100644 src/Serenity.Net.CodeGenerator/Models/Metadata/IRowMetadata.cs create mode 100644 src/Serenity.Net.CodeGenerator/Models/Metadata/IRowPropertyMetadata.cs rename src/Serenity.Net.CodeGenerator/Models/{TypeNameRef.cs => TypeOfRef.cs} (81%) diff --git a/src/Serenity.Net.CodeGenerator/Commands/GenerateCommand.cs b/src/Serenity.Net.CodeGenerator/Commands/GenerateCommand.cs index abc0c5bd5b..77d7574780 100644 --- a/src/Serenity.Net.CodeGenerator/Commands/GenerateCommand.cs +++ b/src/Serenity.Net.CodeGenerator/Commands/GenerateCommand.cs @@ -113,7 +113,7 @@ public ExitCodes Run(string csproj, string[] args) ConnectionKey = connectionKey, Config = config, Schema = tableEntry.Schema, - Table = tableEntry.Tablename, + Table = tableEntry.Table, Module = module, Identifier = identifier, PermissionKey = permissionKey @@ -136,12 +136,25 @@ public ExitCodes Run(string csproj, string[] args) config.GenerateCustom = whatToGenerate.Contains("Custom", StringComparer.Ordinal); } + IApplicationMetadata application = null; + try + { + var assemblyFiles = ServerTypingsCommand.DetermineAssemblyFiles(fileSystem, csproj, config, (error) => { }); + if (assemblyFiles != null && assemblyFiles.Length > 0) + { + application = new ApplicationMetadata(fileSystem, assemblyFiles); + } + } + catch { } + foreach (var inputs in inputsList) { UpdateConfigTableFor(inputs, confConnection); + inputs.Application = application; + var generator = CreateCodeGenerator(inputs, new EntityModelGenerator(), - csproj, fileSystem, sqlConnections, interactive: argsIdentifier is null); + csproj, fileSystem, sqlConnections, interactive: true); generator.Run(); } diff --git a/src/Serenity.Net.CodeGenerator/Generator/EntityModelGenerator.cs b/src/Serenity.Net.CodeGenerator/Generator/EntityModelGenerator.cs index 2af9d38bdf..54cd507109 100644 --- a/src/Serenity.Net.CodeGenerator/Generator/EntityModelGenerator.cs +++ b/src/Serenity.Net.CodeGenerator/Generator/EntityModelGenerator.cs @@ -394,7 +394,13 @@ void makeUniquePropertyName(EntityField f) if (!string.IsNullOrEmpty(tableField.PKTable)) { var pkTable = string.IsNullOrEmpty(tableField.PKSchema) ? tableField.PKTable : ("[" + tableField.PKSchema + "].[" + tableField.PKTable + "]"); - attrs.Add(new("Serenity.Data.Mapping.ForeignKey", pkTable, tableField.PKColumn)); + var pkRow = inputs.Application?.GetRowByTablename(pkTable); + if (pkRow != null) + { + attrs.Add(new("Serenity.Data.Mapping.ForeignKey", new TypeOfRef(pkRow.FullName))); + } + else + attrs.Add(new("Serenity.Data.Mapping.ForeignKey", pkTable, tableField.PKColumn)); object alias = model.DeclareJoinConstants ? new RawCode(tableField.ForeignJoinAlias) : tableField.ForeignJoinAlias; diff --git a/src/Serenity.Net.CodeGenerator/Models/AttributeTypeRef.cs b/src/Serenity.Net.CodeGenerator/Models/AttributeTypeRef.cs index 13979bccd7..e373b43b2b 100644 --- a/src/Serenity.Net.CodeGenerator/Models/AttributeTypeRef.cs +++ b/src/Serenity.Net.CodeGenerator/Models/AttributeTypeRef.cs @@ -24,7 +24,7 @@ public string ToString(CodeWriter cw) if (i > 0) s += ", "; - if (value is TypeNameRef tr) + if (value is TypeOfRef tr) s += tr.ToString(cw); else if (value is string str) s += StringHelper.ToDoubleQuoted(str); diff --git a/src/Serenity.Net.CodeGenerator/Models/EntityModelInputs.cs b/src/Serenity.Net.CodeGenerator/Models/EntityModelInputs.cs index 5436dd39fd..4ffbdf67ec 100644 --- a/src/Serenity.Net.CodeGenerator/Models/EntityModelInputs.cs +++ b/src/Serenity.Net.CodeGenerator/Models/EntityModelInputs.cs @@ -1,7 +1,8 @@ -namespace Serenity.CodeGenerator; +namespace Serenity.CodeGenerator; public class EntityModelInputs : IEntityModelInputs { + public IApplicationMetadata Application { get; set; } public GeneratorConfig Config { get; set; } public string ConnectionKey { get; set; } public IEntityDataSchema DataSchema { get; set; } diff --git a/src/Serenity.Net.CodeGenerator/Models/IEntityModelInputs.cs b/src/Serenity.Net.CodeGenerator/Models/IEntityModelInputs.cs index 63c240b055..29861bafed 100644 --- a/src/Serenity.Net.CodeGenerator/Models/IEntityModelInputs.cs +++ b/src/Serenity.Net.CodeGenerator/Models/IEntityModelInputs.cs @@ -1,7 +1,8 @@ -namespace Serenity.CodeGenerator; +namespace Serenity.CodeGenerator; public interface IEntityModelInputs { + public IApplicationMetadata Application { get; } string ConnectionKey { get; } string Identifier { get; } GeneratorConfig Config { get; } diff --git a/src/Serenity.Net.CodeGenerator/Models/Metadata/ApplicationMetadata.cs b/src/Serenity.Net.CodeGenerator/Models/Metadata/ApplicationMetadata.cs new file mode 100644 index 0000000000..fa60bba6a9 --- /dev/null +++ b/src/Serenity.Net.CodeGenerator/Models/Metadata/ApplicationMetadata.cs @@ -0,0 +1,118 @@ +using Serenity.CodeGeneration; + +namespace Serenity.CodeGenerator; + +public class ApplicationMetadata : IApplicationMetadata +{ + private class Scanner : ServerTypingsGenerator + { + public List RowTypes { get; } = new(); + + public Scanner(IGeneratorFileSystem fileSystem, params string[] assemblyLocations) + : base(fileSystem, assemblyLocations) + { + } + + protected override void GenerateCodeFor(TypeDefinition type) + { + if (TypingsUtils.IsSubclassOf(type, "Serenity.Data", "Row") || + TypingsUtils.IsSubclassOf(type, "Serenity.Data", "Row`1")) + { + RowTypes.Add(type); + } + } + + protected override void HandleMemberType(TypeReference memberType, string codeNamespace, bool module) + { + } + } + + private readonly Scanner scanner; + + public ApplicationMetadata(IGeneratorFileSystem fileSystem, params string[] assemblyLocations) + { + scanner = new Scanner(fileSystem, assemblyLocations); + scanner.Run(); + } + + string DefaultSchema { get; set; } + + private string ParseSchemaAndName(string objectName, out string schema) + { + if (objectName is null) + throw new ArgumentNullException(nameof(objectName)); + + schema = DefaultSchema; + var parts = objectName.Split('.'); + if (parts.Length <= 0 || parts.Length > 2) + return null; + + if (parts.Length == 1) + return SqlSyntax.Unquote(parts[0]); + + schema = SqlSyntax.Unquote(parts[0]); + return SqlSyntax.Unquote(parts[1]); + } + + private string NormalizeTablename(string objectName) + { + var table = ParseSchemaAndName(objectName, out var schema); + return table + "." + schema; + } + + private bool IsEqualIgnoreCase(string objectName1, string objectName2) + { + return !string.IsNullOrEmpty(objectName1) && + !string.IsNullOrEmpty(objectName2) && + string.Equals(NormalizeTablename(objectName1), + NormalizeTablename(objectName2), StringComparison.OrdinalIgnoreCase); + } + + public IRowMetadata GetRowByTablename(string tablename) + { + foreach (var type in scanner.RowTypes) + { + var attr = type.GetAttributes().FirstOrDefault(x => + x.AttributeType().Name == "TableNameAttribute" && + x.AttributeType().NamespaceOf() == "Serenity.Data.Mapping"); + + if (attr != null) + { + if (IsEqualIgnoreCase(tablename, attr?.ConstructorArguments?.FirstOrDefault().Value as string)) + return new RowMetadata(type); + + continue; + } + + var name = type.Name; + if (name.EndsWith("Row", StringComparison.Ordinal)) + name = name[..^3]; + + if (IsEqualIgnoreCase(tablename, name)) + return new RowMetadata(type); + } + + return null; + } + + private class RowMetadata : IRowMetadata + { + private readonly TypeDefinition type; + + public RowMetadata(TypeDefinition type) + { + this.type = type ?? throw new ArgumentNullException(nameof(type)); + } + + public string Namespace => type.NamespaceOf(); + + public string ClassName => type.Name; + + public string FullName => type.FullNameOf(); + + public IRowPropertyMetadata GetTableField(string columnName) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Serenity.Net.CodeGenerator/Models/Metadata/IApplicationMetadata.cs b/src/Serenity.Net.CodeGenerator/Models/Metadata/IApplicationMetadata.cs new file mode 100644 index 0000000000..278d3b780d --- /dev/null +++ b/src/Serenity.Net.CodeGenerator/Models/Metadata/IApplicationMetadata.cs @@ -0,0 +1,6 @@ +namespace Serenity.CodeGenerator; + +public interface IApplicationMetadata +{ + IRowMetadata GetRowByTablename(string tablename); +} \ No newline at end of file diff --git a/src/Serenity.Net.CodeGenerator/Models/Metadata/IClassMetadata.cs b/src/Serenity.Net.CodeGenerator/Models/Metadata/IClassMetadata.cs new file mode 100644 index 0000000000..bfb85834ab --- /dev/null +++ b/src/Serenity.Net.CodeGenerator/Models/Metadata/IClassMetadata.cs @@ -0,0 +1,8 @@ +namespace Serenity.CodeGenerator; + +public interface IClassMetadata +{ + string Namespace { get; } + string ClassName { get; } + string FullName => string.IsNullOrEmpty(Namespace) ? ClassName : Namespace + "." + ClassName; +} \ No newline at end of file diff --git a/src/Serenity.Net.CodeGenerator/Models/Metadata/IPropertyMetadata.cs b/src/Serenity.Net.CodeGenerator/Models/Metadata/IPropertyMetadata.cs new file mode 100644 index 0000000000..d58093c28b --- /dev/null +++ b/src/Serenity.Net.CodeGenerator/Models/Metadata/IPropertyMetadata.cs @@ -0,0 +1,6 @@ +namespace Serenity.CodeGenerator; + +public interface IPropertyMetadata +{ + string PropertyName { get; } +} \ No newline at end of file diff --git a/src/Serenity.Net.CodeGenerator/Models/Metadata/IRowMetadata.cs b/src/Serenity.Net.CodeGenerator/Models/Metadata/IRowMetadata.cs new file mode 100644 index 0000000000..309d25bf20 --- /dev/null +++ b/src/Serenity.Net.CodeGenerator/Models/Metadata/IRowMetadata.cs @@ -0,0 +1,6 @@ +namespace Serenity.CodeGenerator; + +public interface IRowMetadata : IClassMetadata +{ + IRowPropertyMetadata GetTableField(string columnName); +} \ No newline at end of file diff --git a/src/Serenity.Net.CodeGenerator/Models/Metadata/IRowPropertyMetadata.cs b/src/Serenity.Net.CodeGenerator/Models/Metadata/IRowPropertyMetadata.cs new file mode 100644 index 0000000000..427d4fb7a0 --- /dev/null +++ b/src/Serenity.Net.CodeGenerator/Models/Metadata/IRowPropertyMetadata.cs @@ -0,0 +1,5 @@ +namespace Serenity.CodeGenerator; + +public interface IRowPropertyMetadata : IPropertyMetadata +{ +} \ No newline at end of file diff --git a/src/Serenity.Net.CodeGenerator/Models/TypeNameRef.cs b/src/Serenity.Net.CodeGenerator/Models/TypeOfRef.cs similarity index 81% rename from src/Serenity.Net.CodeGenerator/Models/TypeNameRef.cs rename to src/Serenity.Net.CodeGenerator/Models/TypeOfRef.cs index 2421241ce5..fa5567d60c 100644 --- a/src/Serenity.Net.CodeGenerator/Models/TypeNameRef.cs +++ b/src/Serenity.Net.CodeGenerator/Models/TypeOfRef.cs @@ -1,10 +1,10 @@ namespace Serenity.CodeGenerator; -public class TypeNameRef +public class TypeOfRef { public string TypeName { get; } - public TypeNameRef(string typeName) + public TypeOfRef(string typeName) { TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName)); } diff --git a/src/Serenity.Net.Core/Reflection/CodeWriter.cs b/src/Serenity.Net.Core/Reflection/CodeWriter.cs index 5405bb3088..c726b07eb3 100644 --- a/src/Serenity.Net.Core/Reflection/CodeWriter.cs +++ b/src/Serenity.Net.Core/Reflection/CodeWriter.cs @@ -351,8 +351,13 @@ public string ShortTypeName(string ns, string typeName) if (Using(ns)) return typeName; - else - return ns + "." + typeName; + else if (CurrentNamespace != null) + { + var idx = CurrentNamespace.IndexOf('.', StringComparison.Ordinal); + if (idx >= 0 && ns.StartsWith(CurrentNamespace[..(idx + 1)], StringComparison.Ordinal)) + ns = ns[(idx + 1)..]; + } + return ns + "." + typeName; } ///