Skip to content

Commit

Permalink
Add a way to configure identity columns and triggers for split enities
Browse files Browse the repository at this point in the history
Add support for annotations to mapping fragments, property overrides and sequences
Update back-reference on mapping fragments and overrides when reattaching

Fixes dotnet#28195
Fixes dotnet#28237
  • Loading branch information
AndriySvyryd authored Jul 1, 2022
1 parent b121ee3 commit 9952893
Show file tree
Hide file tree
Showing 59 changed files with 2,289 additions and 332 deletions.
166 changes: 111 additions & 55 deletions src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,80 +316,103 @@ protected virtual void GenerateSequence(
ISequence sequence,
IndentedStringBuilder stringBuilder)
{
stringBuilder
.AppendLine()
var sequenceBuilderNameBuilder = new StringBuilder();
sequenceBuilderNameBuilder
.Append(modelBuilderName)
.Append(".HasSequence");

if (sequence.Type != Sequence.DefaultClrType)
{
stringBuilder
sequenceBuilderNameBuilder
.Append("<")
.Append(Code.Reference(sequence.Type))
.Append(">");
}

stringBuilder
sequenceBuilderNameBuilder
.Append("(")
.Append(Code.Literal(sequence.Name));

if (!string.IsNullOrEmpty(sequence.Schema)
&& sequence.Model.GetDefaultSchema() != sequence.Schema)
{
stringBuilder
sequenceBuilderNameBuilder
.Append(", ")
.Append(Code.Literal(sequence.Schema));
}

stringBuilder.Append(")");
sequenceBuilderNameBuilder.Append(")");
var sequenceBuilderName = sequenceBuilderNameBuilder.ToString();

using (stringBuilder.Indent())
stringBuilder
.AppendLine()
.Append(sequenceBuilderName);

// Note that GenerateAnnotations below does the corresponding decrement
stringBuilder.IncrementIndent();

if (sequence.StartValue != Sequence.DefaultStartValue)
{
if (sequence.StartValue != Sequence.DefaultStartValue)
{
stringBuilder
.AppendLine()
.Append(".StartsAt(")
.Append(Code.Literal(sequence.StartValue))
.Append(")");
}
stringBuilder
.AppendLine()
.Append(".StartsAt(")
.Append(Code.Literal(sequence.StartValue))
.Append(")");
}

if (sequence.IncrementBy != Sequence.DefaultIncrementBy)
{
stringBuilder
.AppendLine()
.Append(".IncrementsBy(")
.Append(Code.Literal(sequence.IncrementBy))
.Append(")");
}
if (sequence.IncrementBy != Sequence.DefaultIncrementBy)
{
stringBuilder
.AppendLine()
.Append(".IncrementsBy(")
.Append(Code.Literal(sequence.IncrementBy))
.Append(")");
}

if (sequence.MinValue != Sequence.DefaultMinValue)
{
stringBuilder
.AppendLine()
.Append(".HasMin(")
.Append(Code.Literal(sequence.MinValue))
.Append(")");
}
if (sequence.MinValue != Sequence.DefaultMinValue)
{
stringBuilder
.AppendLine()
.Append(".HasMin(")
.Append(Code.Literal(sequence.MinValue))
.Append(")");
}

if (sequence.MaxValue != Sequence.DefaultMaxValue)
{
stringBuilder
.AppendLine()
.Append(".HasMax(")
.Append(Code.Literal(sequence.MaxValue))
.Append(")");
}
if (sequence.MaxValue != Sequence.DefaultMaxValue)
{
stringBuilder
.AppendLine()
.Append(".HasMax(")
.Append(Code.Literal(sequence.MaxValue))
.Append(")");
}

if (sequence.IsCyclic != Sequence.DefaultIsCyclic)
{
stringBuilder
.AppendLine()
.Append(".IsCyclic()");
}
if (sequence.IsCyclic != Sequence.DefaultIsCyclic)
{
stringBuilder
.AppendLine()
.Append(".IsCyclic()");
}

stringBuilder.AppendLine(";");
GenerateSequenceAnnotations(sequenceBuilderName, sequence, stringBuilder);
}

/// <summary>
/// Generates code for sequence annotations.
/// </summary>
/// <param name="sequenceBuilderName">The name of the sequence builder variable.</param>
/// <param name="sequence">The sequence.</param>
/// <param name="stringBuilder">The builder code is added to.</param>
protected virtual void GenerateSequenceAnnotations(
string sequenceBuilderName,
ISequence sequence,
IndentedStringBuilder stringBuilder)
{
var annotations = Dependencies.AnnotationCodeGenerator
.FilterIgnoredAnnotations(sequence.GetAnnotations())
.ToDictionary(a => a.Name, a => a);

GenerateAnnotations(sequenceBuilderName, sequence, stringBuilder, annotations, inChainedCall: true);
}

/// <summary>
Expand Down Expand Up @@ -849,7 +872,7 @@ private void GenerateTableMapping(
annotations.Remove(isExcludedAnnotation.Name);
}

var hasTriggers = entityType.GetTriggers().Any();
var hasTriggers = entityType.GetTriggers().Any(t => t.TableName == tableName! && t.TableSchema == schema);
var hasOverrides = table != null
&& entityType.GetProperties().Select(p => p.FindOverrides(table.Value)).Any(o => o != null);
var requiresTableBuilder = isExcludedFromMigrations
Expand Down Expand Up @@ -889,8 +912,7 @@ private void GenerateTableMapping(

if (hasTriggers)
{
stringBuilder.AppendLine();
GenerateTriggers("t", entityType, stringBuilder);
GenerateTriggers("t", entityType, tableName!, schema, stringBuilder);
}

if (hasOverrides)
Expand All @@ -914,13 +936,14 @@ private void GenerateSplitTableMapping(
{
foreach (var fragment in entityType.GetMappingFragments(StoreObjectType.Table))
{
var table = fragment.StoreObject;
stringBuilder
.AppendLine()
.Append(entityTypeBuilderName)
.Append(".SplitToTable(")
.Append(Code.UnknownLiteral(fragment.StoreObject.Name))
.Append(Code.UnknownLiteral(table.Name))
.Append(", ")
.Append(Code.UnknownLiteral(fragment.StoreObject.Schema))
.Append(Code.UnknownLiteral(table.Schema))
.AppendLine(", t =>");

using (stringBuilder.Indent())
Expand All @@ -929,7 +952,9 @@ private void GenerateSplitTableMapping(

using (stringBuilder.Indent())
{
GenerateOverrides("t", entityType, fragment.StoreObject, stringBuilder);
GenerateTriggers("t", entityType, table.Name, table.Schema, stringBuilder);
GenerateOverrides("t", entityType, table, stringBuilder);
GenerateEntityTypeMappingFragmentAnnotations("t", fragment, stringBuilder);
}

stringBuilder
Expand Down Expand Up @@ -1025,6 +1050,7 @@ private void GenerateSplitViewMapping(
using (stringBuilder.Indent())
{
GenerateOverrides("v", entityType, fragment.StoreObject, stringBuilder);
GenerateEntityTypeMappingFragmentAnnotations("v", fragment, stringBuilder);
}

stringBuilder
Expand All @@ -1034,6 +1060,26 @@ private void GenerateSplitViewMapping(
}
}

/// <summary>
/// Generates code for mapping fragment annotations.
/// </summary>
/// <param name="tableBuilderName">The name of the table builder variable.</param>
/// <param name="fragment">The mapping fragment.</param>
/// <param name="stringBuilder">The builder code is added to.</param>
protected virtual void GenerateEntityTypeMappingFragmentAnnotations(
string tableBuilderName,
IEntityTypeMappingFragment fragment,
IndentedStringBuilder stringBuilder)
{
var annotations = Dependencies.AnnotationCodeGenerator
.FilterIgnoredAnnotations(fragment.GetAnnotations())
.ToDictionary(a => a.Name, a => a);
if (annotations.Count > 0)
{
GenerateAnnotations(tableBuilderName, fragment, stringBuilder, annotations, inChainedCall: false);
}
}

/// <summary>
/// Generates code for <see cref="ICheckConstraint" /> objects.
/// </summary>
Expand Down Expand Up @@ -1137,14 +1183,23 @@ protected virtual void GenerateCheckConstraintAnnotations(
/// </summary>
/// <param name="tableBuilderName">The name of the table builder variable.</param>
/// <param name="entityType">The entity type.</param>
/// <param name="table">The table name.</param>
/// <param name="schema">The table schema.</param>
/// <param name="stringBuilder">The builder code is added to.</param>
protected virtual void GenerateTriggers(
string tableBuilderName,
IEntityType entityType,
string table,
string? schema,
IndentedStringBuilder stringBuilder)
{
foreach (var trigger in entityType.GetTriggers())
{
if (trigger.TableName != table || trigger.TableSchema != schema)
{
continue;
}

GenerateTrigger(tableBuilderName, trigger, stringBuilder);
}
}
Expand All @@ -1161,13 +1216,14 @@ protected virtual void GenerateTrigger(
IndentedStringBuilder stringBuilder)
{
var triggerBuilderName = $"{tableBuilderName}.HasTrigger({Code.Literal(trigger.ModelName)})";
stringBuilder.Append(triggerBuilderName);
stringBuilder
.AppendLine()
.Append(triggerBuilderName);

// Note that GenerateAnnotations below does the corresponding decrement
stringBuilder.IncrementIndent();

if (trigger.Name != null
&& trigger.Name != (trigger.GetDefaultName() ?? trigger.ModelName))
if (trigger.Name != trigger.GetDefaultName()!)
{
stringBuilder
.AppendLine()
Expand Down
60 changes: 33 additions & 27 deletions src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -433,57 +433,63 @@ private void GenerateEntityType(IEntityType entityType)
{
_builder.AppendLine();

_builder.Append($"{EntityLambdaIdentifier}.{nameof(RelationalEntityTypeBuilderExtensions.ToTable)}(tb => ");
_builder.AppendLine($"{EntityLambdaIdentifier}.{nameof(RelationalEntityTypeBuilderExtensions.ToTable)}(tb =>");

// Note: no trigger annotation support as of yet

if (triggers.Length == 1)
{
var trigger = triggers[0];
if (trigger.Name is not null)
{
_builder.AppendLine($"tb.HasTrigger({_code.Literal(trigger.Name)}));");
}
}
else
using (_builder.Indent())
{
_builder.AppendLine("{");

using (_builder.Indent())
_builder.Append("{");
foreach (var trigger in entityType.GetTriggers().Where(t => t.Name is not null))
{
foreach (var trigger in entityType.GetTriggers().Where(t => t.Name is not null))
{
_builder.AppendLine($"tb.HasTrigger({_code.Literal(trigger.Name!)});");
}
GenerateTrigger("tb", trigger);
}

_builder.AppendLine("});");
}

}
}
}

private void AppendMultiLineFluentApi(IEntityType entityType, IList<string> lines)
private void GenerateTrigger(string tableBuilderName, ITrigger trigger)
{
var lines = new List<string> { $".HasTrigger({_code.Literal(trigger.Name!)})" };

var annotations = _annotationCodeGenerator
.FilterIgnoredAnnotations(trigger.GetAnnotations())
.ToDictionary(a => a.Name, a => a);

_annotationCodeGenerator.RemoveAnnotationsHandledByConventions(trigger, annotations);

GenerateAnnotations(trigger, annotations, lines);

AppendMultiLineFluentApi(null, lines, tableBuilderName);
}

private void AppendMultiLineFluentApi(IEntityType? entityType, IList<string> lines, string? builderName = null)
{
if (lines.Count <= 0)
{
return;
}

InitializeEntityTypeBuilder(entityType);
if (entityType != null)
{
InitializeEntityTypeBuilder(entityType);
}

using (_builder.Indent())
{
_builder.AppendLine();

_builder.Append(EntityLambdaIdentifier + lines[0]);
_builder
.AppendLine()
.Append(builderName ?? EntityLambdaIdentifier)
.Append(lines[0]);

using (_builder.Indent())
{
foreach (var line in lines.Skip(1))
{
_builder.AppendLine();
_builder.Append(line);
_builder
.AppendLine()
.Append(line);
}
}

Expand Down
Loading

0 comments on commit 9952893

Please sign in to comment.