Skip to content

Commit

Permalink
Also use metadata for currently generating rows when multiple tables …
Browse files Browse the repository at this point in the history
…are selected in source generator.
  • Loading branch information
volkanceylan committed Oct 19, 2023
1 parent cec0461 commit d65e488
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 55 deletions.
23 changes: 17 additions & 6 deletions src/Serenity.Net.CodeGenerator/Commands/GenerateCommand.Shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ private static void UpdateConfigTableFor(EntityModelInputs inputs,
}
}

private static EntityCodeGenerator CreateCodeGenerator(
EntityModelInputs inputs, IEntityModelGenerator modelGenerator,
string csproj, IGeneratorFileSystem fileSystem,
ISqlConnections sqlConnections, bool interactive = true)
private static EntityModel CreateEntityModel(EntityModelInputs inputs,
IEntityModelGenerator modelGenerator,
string csproj,
IGeneratorFileSystem fileSystem,
ISqlConnections sqlConnections)
{
using var connection = sqlConnections.NewByKey(inputs.ConnectionKey);
connection.EnsureOpen();
Expand All @@ -58,7 +59,17 @@ private static EntityCodeGenerator CreateCodeGenerator(
StringComparison.OrdinalIgnoreCase);

inputs.DataSchema = new EntityDataSchema(connection);
var rowModel = modelGenerator.GenerateModel(inputs);
return modelGenerator.GenerateModel(inputs);
}

private static EntityCodeGenerator CreateCodeGenerator(EntityModelInputs inputs,
IEntityModelGenerator modelGenerator,
string csproj,
IGeneratorFileSystem fileSystem,
ISqlConnections sqlConnections,
bool interactive = true)
{
var entityModel = CreateEntityModel(inputs, modelGenerator, csproj, fileSystem, sqlConnections);

var codeFileHelper = new CodeFileHelper(fileSystem)
{
Expand All @@ -67,7 +78,7 @@ private static EntityCodeGenerator CreateCodeGenerator(
TSCPath = inputs.Config.TSCPath ?? "tsc"
};

return new EntityCodeGenerator(fileSystem, codeFileHelper, rowModel, inputs.Config, csproj);
return new EntityCodeGenerator(fileSystem, codeFileHelper, entityModel, inputs.Config, csproj);
}

private static void RegisterSqlProviders()
Expand Down
21 changes: 19 additions & 2 deletions src/Serenity.Net.CodeGenerator/Commands/GenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,30 @@ public ExitCodes Run(string csproj, string[] args)
config.GenerateCustom = whatToGenerate.Contains("Custom", StringComparer.Ordinal);
}

IApplicationMetadata application = null;
ApplicationMetadata application = null;
try
{
var assemblyFiles = ServerTypingsCommand.DetermineAssemblyFiles(fileSystem, csproj, config, (error) => { });
if (assemblyFiles != null && assemblyFiles.Length > 0)
{
application = new ApplicationMetadata(fileSystem, assemblyFiles);
application = new ApplicationMetadata(fileSystem, assemblyFiles)
{
DefaultSchema = schemaProvider.DefaultSchema
};

foreach (var inputs in inputsList)
{
inputs.SkipForeignKeys = true;
try
{
var entityModel = CreateEntityModel(inputs, new EntityModelGenerator(), csproj, fileSystem, sqlConnections);
application.EntityModels.Add(entityModel);
}
finally
{
inputs.SkipForeignKeys = false;
}
}
}
}
catch { }
Expand Down
49 changes: 25 additions & 24 deletions src/Serenity.Net.CodeGenerator/Generator/EntityModelGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,27 @@ string normalizeSchema(string name)
field.IsIdentity = identities.Contains(field.FieldName);
}

var foreignKeyInfos = inputs.DataSchema.GetForeignKeys(inputs.Schema, inputs.Table)
.ToLookup(x => x.FKName)
.Where(x => x.Count() == 1)
.SelectMany(x => x)
.ToList();
var prefix = DeterminePrefixLength(fieldInfos, x => x.FieldName);
model.FieldPrefix = prefix > 0 ? fieldInfos.First().FieldName[..prefix] : "";

var idFieldInfo = fieldInfos.FirstOrDefault(f => f.IsIdentity == true);
idFieldInfo ??= fieldInfos.FirstOrDefault(f => f.IsPrimaryKey == true);
if (idFieldInfo != null)
model.IdField = PropertyNameFor(idFieldInfo.FieldName[prefix..]);
else
{
idFieldInfo = fieldInfos.FirstOrDefault(f => f.IsPrimaryKey == true) ??
fieldInfos.FirstOrDefault();
if (idFieldInfo != null)
model.IdField = PropertyNameFor(idFieldInfo.FieldName[prefix..]);
}

var foreignKeyInfos = !inputs.SkipForeignKeys ?
inputs.DataSchema.GetForeignKeys(inputs.Schema, inputs.Table)
.ToLookup(x => x.FKName)
.Where(x => x.Count() == 1)
.SelectMany(x => x)
.ToList() : new();

foreach (var field in fieldInfos)
{
Expand All @@ -156,21 +172,6 @@ string normalizeSchema(string name)
}
}

var prefix = DeterminePrefixLength(fieldInfos, x => x.FieldName);
model.FieldPrefix = prefix > 0 ? fieldInfos.First().FieldName[..prefix] : "";

var identity = fieldInfos.FirstOrDefault(f => f.IsIdentity == true);
identity ??= fieldInfos.FirstOrDefault(f => f.IsPrimaryKey == true);
if (identity != null)
model.Identity = PropertyNameFor(identity.FieldName[prefix..]);
else
{
identity = fieldInfos.FirstOrDefault(f => f.IsPrimaryKey == true) ??
fieldInfos.FirstOrDefault();
if (identity != null)
model.Identity = PropertyNameFor(identity.FieldName[prefix..]);
}

string baseRowMatch = null;
HashSet<string> baseRowFieldset = null;
List<string> baseRowFieldList = new();
Expand Down Expand Up @@ -220,14 +221,14 @@ string normalizeSchema(string name)

var foreignSelection = inputs.Config.ForeignFieldSelection ?? GeneratorConfig.FieldSelection.All;

if (inputs.Config.RemoveForeignFields is { } removeFK)
if (!inputs.SkipForeignKeys && inputs.Config.RemoveForeignFields is { } removeFK)
{
removeForeignFields.AddRange(removeFK.Select(
x => x.TrimToNull()).Where(x => x != null));
}

var includeForeignFields = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (inputs.Config.IncludeForeignFields is { } includeFK)
if (!inputs.SkipForeignKeys && inputs.Config.IncludeForeignFields is { } includeFK)
{
includeForeignFields.AddRange(includeFK.Select(
x => x.TrimToNull()).Where(x => x != null));
Expand Down Expand Up @@ -269,7 +270,7 @@ void makeUniquePropertyName(EntityField f)
{
var tableField = ToEntityField(fieldInfo, prefix, includeFlags: true);

if (tableField.PropertyName == model.Identity)
if (tableField.PropertyName == model.IdField)
{
tableField.ColAttributeList.Add(new("Serenity.ComponentModel.EditLink"));
tableField.ColAttributeList.Add(new("System.ComponentModel.DisplayName", "Db.Shared.RecordId"));
Expand Down Expand Up @@ -455,7 +456,7 @@ void makeUniquePropertyName(EntityField f)
IRowMetadata pkRow = null;
IRowPropertyMetadata pkProperty = null;
bool pkPropertyIsId = false;
if (!string.IsNullOrEmpty(tableField.PKTable))
if (!inputs.SkipForeignKeys && !string.IsNullOrEmpty(tableField.PKTable))
{
var pkTable = string.IsNullOrEmpty(tableField.PKSchema) ? tableField.PKTable :
("[" + tableField.PKSchema + "].[" + tableField.PKTable + "]");
Expand Down
8 changes: 4 additions & 4 deletions src/Serenity.Net.CodeGenerator/Models/EntityModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class EntityModel
public string Schema { get; set; }
public string Tablename { get; set; }
public string Title { get; set; }
public string Identity { get; set; }
public string IdField { get; set; }
public string RowBaseClass { get; set; } = "Serenity.Data.Row";
public List<EntityField> RowBaseFields { get; } = new();
public string FieldsBaseClass { get; set; } = "Serenity.Data.RowFieldsBase";
Expand All @@ -29,7 +29,7 @@ public class EntityModel
public bool GenerateListExcel { get; set; }
public HashSet<string> GlobalUsings { get; } = new();

public string IdField => Identity;
public string Identity => IdField;
public Dictionary<string, object> CustomSettings { get; set; }

public IEnumerable<EntityField> FormFields => Fields.Where(f => !f.OmitInForm);
Expand Down Expand Up @@ -113,7 +113,7 @@ public string NavigationCategory

public string SchemaAndTable
{
get { return string.IsNullOrEmpty(Schema)? Tablename : "[" + Schema + "].[" + Tablename + "]"; }
get { return string.IsNullOrEmpty(Schema) ? Tablename : "[" + Schema + "].[" + Tablename + "]"; }
}

public string RowBaseClassAndInterfaces
Expand Down Expand Up @@ -141,7 +141,7 @@ public List<string> RowBaseClassAndInterfaceList
{
var result = new List<string> { RowBaseClass ?? "Serenity.Data.Row" };

if (!string.IsNullOrEmpty(Identity))
if (!string.IsNullOrEmpty(IdField))
result.Add("Serenity.Data.IIdRow");
if (!string.IsNullOrEmpty(NameField))
result.Add("Serenity.Data.INameRow");
Expand Down
1 change: 1 addition & 0 deletions src/Serenity.Net.CodeGenerator/Models/EntityModelInputs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ public class EntityModelInputs : IEntityModelInputs
public string PermissionKey { get; set; }
public string Schema { get; set; }
public string Table { get; set; }
public bool SkipForeignKeys { get; set; }
}
2 changes: 2 additions & 0 deletions src/Serenity.Net.CodeGenerator/Models/IEntityModelInputs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ public interface IEntityModelInputs
bool SchemaIsDatabase { get; }
string PermissionKey { get; }
string Schema { get; }
bool SkipForeignKeys { get; }
string Table { get; }
string Tablename => string.IsNullOrEmpty(Schema) ? Table : (Schema + "." + Table);
}
108 changes: 91 additions & 17 deletions src/Serenity.Net.CodeGenerator/Models/Metadata/ApplicationMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ public ApplicationMetadata(IGeneratorFileSystem fileSystem, params string[] asse
scanner.Run();
}

string DefaultSchema { get; set; }
public List<EntityModel> EntityModels { get; } = new List<EntityModel>();
public string DefaultSchema { get; set; }

private string ParseSchemaAndName(string objectName, out string schema)
{
Expand Down Expand Up @@ -105,16 +106,22 @@ private bool IsEqualIgnoreCase(string objectName1, string objectName2)
NormalizeTablename(objectName2), StringComparison.OrdinalIgnoreCase);
}

private readonly Dictionary<string, RowMetadata> rowByTablename = new();
private readonly Dictionary<string, IRowMetadata> rowByTablename = new();

public IRowMetadata GetRowByTablename(string tablename)
{
if (tablename is null)
throw new ArgumentNullException(nameof(tablename));

if (rowByTablename.TryGetValue(tablename, out RowMetadata metadata))
if (rowByTablename.TryGetValue(tablename, out IRowMetadata metadata))
return metadata;

foreach (var model in EntityModels)
{
if (IsEqualIgnoreCase(tablename, model.SchemaAndTable))
return rowByTablename[tablename] = metadata = new EntityModelRowMetadata(model);
}

foreach (var type in scanner.RowTypes)
{
var attr = type.GetAttributes().FirstOrDefault(x =>
Expand Down Expand Up @@ -215,13 +222,9 @@ public IRowPropertyMetadata GetTableField(string columnName)
StringComparison.OrdinalIgnoreCase));

if (props.Count() == 1)
{
tableFieldByColumnName[columnName] = metadata = new RowPropertyMetadata(props.First());
return metadata;
}
return tableFieldByColumnName[columnName] = new PropertyMetadata(props.First());

tableFieldByColumnName[columnName] = null;
return null;
return tableFieldByColumnName[columnName] = null;
}

private readonly Dictionary<string, IRowPropertyMetadata> propertyByName = new();
Expand All @@ -236,13 +239,9 @@ public IRowPropertyMetadata GetProperty(string name)

var prop = type.PropertiesOf().FirstOrDefault(x => x.Name == name);
if (prop != null)
{
propertyByName[prop.Name] = metadata = new RowPropertyMetadata(prop);
return metadata;
}
return propertyByName[name] = new PropertyMetadata(prop);

propertyByName[prop.Name] = null;
return null;
return propertyByName[name] = null;
}

public string IdProperty
Expand Down Expand Up @@ -277,11 +276,11 @@ public string NameProperty
}
}

public class RowPropertyMetadata : IRowPropertyMetadata
public class PropertyMetadata : IRowPropertyMetadata
{
private readonly PropertyDefinition property;

public RowPropertyMetadata(PropertyDefinition property)
public PropertyMetadata(PropertyDefinition property)
{
this.property = property ?? throw new ArgumentNullException(nameof(property));
}
Expand All @@ -292,4 +291,79 @@ public RowPropertyMetadata(PropertyDefinition property)

public string ListServiceRoute { get; set; }
}

private class EntityModelRowMetadata : IRowMetadata
{
private readonly EntityModel model;

public EntityModelRowMetadata(EntityModel model)
{
this.model = model ?? throw new ArgumentNullException(nameof(model));
}

public bool HasLookupScriptAttribute => false;

public string ListServiceRoute => "Services/" + model.ServiceBaseUrl;

public string IdProperty => model.IdField;

public string NameProperty => model.NameField;

public string Module => model.Module;

public string Namespace => model.ModuleNamespace;

public string ClassName => model.RowClassName;

private readonly Dictionary<string, IRowPropertyMetadata> propertyByName = new();

public IRowPropertyMetadata GetProperty(string name)
{
if (string.IsNullOrEmpty(name))
return null;

if (propertyByName.TryGetValue(name, out IRowPropertyMetadata metadata))
return metadata;

var prop = model.Fields.FirstOrDefault(x => x.PropertyName == name);
if (prop != null)
return propertyByName[name] = new FieldMetadata(prop);

return propertyByName[name] = null;
}

private readonly Dictionary<string, IRowPropertyMetadata> tableFieldByColumnName = new();

public IRowPropertyMetadata GetTableField(string columnName)
{
if (string.IsNullOrEmpty(columnName))
return null;

columnName = SqlSyntax.Unquote(columnName);

if (tableFieldByColumnName.TryGetValue(columnName, out IRowPropertyMetadata metadata))
return metadata;

var props = model.Fields.Where(x => string.Equals(x.Name, columnName,
StringComparison.OrdinalIgnoreCase));

if (props.Count() == 1)
return tableFieldByColumnName[columnName] = new FieldMetadata(props.First());

return tableFieldByColumnName[columnName] = null;
}

public class FieldMetadata : IRowPropertyMetadata
{
private readonly EntityField field;

public FieldMetadata(EntityField field)
{
this.field = field ?? throw new ArgumentNullException(nameof(field));
}

public string ColumnName => field.Name;
public string PropertyName => field.PropertyName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void Customer_Defaults()
Assert.Equal(TestSchema, model.Schema);
Assert.Equal(Customer, model.Tablename);
Assert.Equal(Customer, model.Title);
Assert.Equal(CustomerId, model.Identity);
Assert.Equal(CustomerId, model.IdField);
Assert.Equal(typeof(Row<>).FullName.Split('`')[0] + $"<{Customer}Row.RowFields>", model.RowBaseClass);
Assert.Equal(CustomerName, model.NameField);
Assert.Equal("", model.FieldPrefix);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public CustomerEntityModel(bool joinConstants = false)
Schema = TestSchema;
Tablename = Customer;
Title = Customer;
Identity = CustomerId;
IdField = CustomerId;
RowBaseClass = "Serenity.Data.Row<CustomerRow.RowFields>";
NameField = CustomerName;
FieldPrefix = "";
Expand Down

0 comments on commit d65e488

Please sign in to comment.