Skip to content

Commit

Permalink
fixup! Scaffolding: Create default templates
Browse files Browse the repository at this point in the history
Address some review comments
  • Loading branch information
bricelam committed Aug 4, 2022
1 parent 25f5450 commit 8090d41
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 217 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,6 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
/// </summary>
public static class InternalScaffoldingModelExtensions
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static ICollection<string> GetOrCreateReverseEngineeringErrors(this IMutableModel model)
{
var errors = (ICollection<string>?)model[ScaffoldingAnnotationNames.ReverseEngineeringErrors];
if (errors == null)
{
errors = new List<string>();
model.SetReverseEngineeringErrors(errors);
}

return errors;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static void SetReverseEngineeringErrors(this IMutableModel model, ICollection<string> value)
=> model.SetAnnotation(
ScaffoldingAnnotationNames.ReverseEngineeringErrors,
value);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
52 changes: 40 additions & 12 deletions src/EFCore.Design/Extensions/ScaffoldingModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,15 @@ public static bool IsSimpleManyToManyJoinEntityType(this IEntityType entityType)
}

/// <summary>
/// Gets the errors encountered while reverse engineering the model.
/// Gets a value indicating whether the specified skip navigation represents the left side of the relationship.
/// </summary>
/// <param name="model">The model.</param>
/// <returns>The errors.</returns>
public static IEnumerable<string> GetReverseEngineeringErrors(this IReadOnlyModel model)
=> (IEnumerable<string>?)model[ScaffoldingAnnotationNames.ReverseEngineeringErrors] ?? new List<string>();
/// <param name="skipNavigation">The skip navigation to check.</param>
/// <returns><see langword="true"/> if it represents the left side.</returns>
/// <remarks>
/// The designation of left and right is arbitrary but deterministic. This method exists primarily to avoid configuring the same many-to-many relationship from both of its ends.
/// </remarks>
public static bool IsLeftNavigation(this ISkipNavigation skipNavigation)
=> skipNavigation.JoinEntityType.FindPrimaryKey()!.Properties[0].GetContainingForeignKeys().Single().PrincipalEntityType == skipNavigation.DeclaringEntityType;

/// <summary>
/// Gets the name that should be used for the <see cref="DbSet{TEntity}"/> property on the <see cref="DbContext"/> class for this entity type.
Expand Down Expand Up @@ -317,7 +320,6 @@ public static IEnumerable<AttributeCodeFragment> GetDataAnnotations(this ISkipNa
annotations.Remove(CoreAnnotationNames.ProductVersion);
annotations.Remove(RelationalAnnotationNames.MaxIdentifierLength);
annotations.Remove(ScaffoldingAnnotationNames.DatabaseName);
annotations.Remove(ScaffoldingAnnotationNames.ReverseEngineeringErrors);

var annotationsRoot = GenerateAnnotations(model, annotations, annotationCodeGenerator);
if (annotationsRoot is not null)
Expand Down Expand Up @@ -374,19 +376,45 @@ public static IEnumerable<AttributeCodeFragment> GetDataAnnotations(this ISkipNa
var defaultSchema = entityType.Model.GetDefaultSchema();
var explicitSchema = schema != null && schema != defaultSchema;

var toTableHandledByConventions = true;
var toTableHandledByDataAnnotations = true;

var toTableArguments = new List<object?>();

if (explicitSchema
|| tableName != null && tableName != entityType.GetDbSetName())
{
var toTable = new FluentApiCodeFragment(nameof(RelationalEntityTypeBuilderExtensions.ToTable))
{
Arguments = { tableName },
HasDataAnnotation = true
};
toTableHandledByConventions = false;

toTableArguments.Add(tableName);

if (explicitSchema)
{
toTable.Arguments.Add(schema);
toTableArguments.Add(schema);
}
}

if (entityType.GetTriggers().Any())
{
toTableHandledByConventions = false;
toTableHandledByDataAnnotations = false;

var toTableNestedCalls = new List<MethodCallCodeFragment>();
foreach (var trigger in entityType.GetTriggers())
{
toTableNestedCalls.Add(new MethodCallCodeFragment(nameof(TableBuilder.HasTrigger), trigger.Name));
}

toTableArguments.Add(new NestedClosureCodeFragment("tb", toTableNestedCalls));
}

if (!toTableHandledByConventions)
{
var toTable = new FluentApiCodeFragment(nameof(RelationalEntityTypeBuilderExtensions.ToTable))
{
Arguments = toTableArguments,
HasDataAnnotation = toTableHandledByDataAnnotations
};

root = root?.Chain(toTable) ?? toTable;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@ public static class ScaffoldingAnnotationNames
/// </summary>
public const string Prefix = "Scaffolding:";

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string ReverseEngineeringErrors = Prefix + "ReverseEngineeringErrors";

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
73 changes: 12 additions & 61 deletions src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ public virtual string TransformText()
};

if (NamespaceHint != Options.ModelNamespace
&& !string.IsNullOrEmpty(Options.ModelNamespace)
&& Model.GetEntityTypes().Any()) // TODO: Remove (comment on issue)
&& !string.IsNullOrEmpty(Options.ModelNamespace))
{
usings.Add(Options.ModelNamespace);
}
Expand Down Expand Up @@ -95,16 +94,6 @@ public virtual string TransformText()

}

// TODO: Remove. (API too) Warnings are sufficient
foreach (var error in Model.GetReverseEngineeringErrors())
{

this.Write(" // ");
this.Write(this.ToStringHelper.ToStringWithCulture(error));
this.Write(" Please see the warning messages.\r\n\r\n");

}

if (!Options.SuppressOnConfiguring)
{

Expand All @@ -129,7 +118,6 @@ public virtual string TransformText()
this.Write(" protected override void OnModelCreating(ModelBuilder modelBuilder)\r\n {\r\n");

var anyConfiguration = false;
StringBuilder previousOutput;

var modelFluentApiCalls = Model.GetFluentApiCalls(annotationCodeGenerator);
if (modelFluentApiCalls != null)
Expand All @@ -143,10 +131,11 @@ public virtual string TransformText()
anyConfiguration = true;
}

StringBuilder mainEnvironment;
foreach (var entityType in Model.GetEntityTypes().Where(e => !e.IsSimpleManyToManyJoinEntityType()))
{
// TODO: Describe what this is doing
previousOutput = GenerationEnvironment;
// Save all previously generated code, and start generating into a new temporary environment
mainEnvironment = GenerationEnvironment;
GenerationEnvironment = new StringBuilder();

if (anyConfiguration)
Expand Down Expand Up @@ -233,7 +222,7 @@ public virtual string TransformText()
// TODO: Issue to include things handled by conventions
var propertyFluentApiCalls = property.GetFluentApiCalls(annotationCodeGenerator)
?.FilterChain(c => !(Options.UseDataAnnotations && c.HasDataAnnotation)
// TODO: nameof? Helper methods?
// TODO: Helper methods?
&& !(c.Method == "IsRequired" && !property.ClrType.IsValueType && Options.UseNullableReferenceTypes));
if (propertyFluentApiCalls == null)
{
Expand Down Expand Up @@ -287,17 +276,14 @@ public virtual string TransformText()
}

// TODO: This is complex. Can we combine with the entity type code above? Can we move chunks into helper methods?
foreach (var skipNavigation in entityType.GetSkipNavigations()
// TODO: Helper?
.Where(n => n.JoinEntityType.FindPrimaryKey()!.Properties[0].GetContainingForeignKeys().Single().PrincipalEntityType == entityType))
foreach (var skipNavigation in entityType.GetSkipNavigations().Where(n => n.IsLeftNavigation()))
{
if (anyEntityTypeConfiguration)
{
WriteLine();
}

var left = skipNavigation.ForeignKey;
// TODO: Pass in usings instead of adding below?
var leftFluentApiCalls = left.GetFluentApiCalls(annotationCodeGenerator, useStrings: true);
var right = skipNavigation.Inverse.ForeignKey;
var rightFluentApiCalls = right.GetFluentApiCalls(annotationCodeGenerator, useStrings: true);
Expand Down Expand Up @@ -373,57 +359,22 @@ public virtual string TransformText()

}

// TODO: Is this ever used?
foreach (var property in joinEntityType.GetProperties())
{
var propertyFluentApiCalls = property.GetFluentApiCalls(annotationCodeGenerator)
?.FilterChain(c => !(c.Method == "IsRequired" && Options.UseNullableReferenceTypes && !property.ClrType.IsValueType));
if (propertyFluentApiCalls == null)
{
continue;
}

usings.AddRange(propertyFluentApiCalls.GetRequiredUsings());

this.Write(" j.IndexerProperty<");
this.Write(this.ToStringHelper.ToStringWithCulture(code.Reference(property.ClrType)));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(code.Literal(property.Name)));
this.Write(")");
this.Write(this.ToStringHelper.ToStringWithCulture(code.Fragment(propertyFluentApiCalls, indent: 7)));
this.Write(";\r\n");

}

this.Write(" });\r\n");

anyEntityTypeConfiguration = true;
}

// TODO: Better formatting when multiple. Can we consolidate ToTable calls? Allow null. This should probably be in entityType.GetFluentApiCalls()
foreach (var trigger in entityType.GetTriggers().Where(t => t.Name != null))
{
if (anyEntityTypeConfiguration)
{
WriteLine();
}

this.Write(" entity.ToTable(tb => tb.HasTrigger(");
this.Write(this.ToStringHelper.ToStringWithCulture(code.Literal(trigger.Name)));
this.Write("));\r\n");

anyEntityTypeConfiguration = true;
}

this.Write(" });\r\n");

// If any signicant code was generated, append it to the main environment
if (anyEntityTypeConfiguration)
{
previousOutput.Append(GenerationEnvironment);
mainEnvironment.Append(GenerationEnvironment);
anyConfiguration = true;
}

GenerationEnvironment = previousOutput;
// Resume generating code into the main environment
GenerationEnvironment = mainEnvironment;
}

foreach (var sequence in Model.GetSequences())
Expand Down Expand Up @@ -451,7 +402,7 @@ public virtual string TransformText()
this.Write(" OnModelCreatingPartial(modelBuilder);\r\n }\r\n\r\n partial void OnModelC" +
"reatingPartial(ModelBuilder modelBuilder);\r\n}\r\n");

previousOutput = GenerationEnvironment;
mainEnvironment = GenerationEnvironment;
GenerationEnvironment = new StringBuilder();

foreach (var ns in usings.Distinct().OrderBy(x => x, new NamespaceComparer()))
Expand All @@ -465,7 +416,7 @@ public virtual string TransformText()

WriteLine();

GenerationEnvironment.Append(previousOutput);
GenerationEnvironment.Append(mainEnvironment);

return this.GenerationEnvironment.ToString();
}
Expand Down
Loading

0 comments on commit 8090d41

Please sign in to comment.