Skip to content

Commit

Permalink
Temporal Tables - metadata and migrations part
Browse files Browse the repository at this point in the history
- added model builder APIs that allows mapping entity to a temporal table as well as related configuration
- added conventions and validation for entities mapped to temporal table
- added migration support for adding/removing/modifying entities mapped to a temporal table
- added model snapshot generation for entities mapped to temporal table
  • Loading branch information
maumar committed Jun 4, 2021
1 parent fa4b179 commit e7c0b9d
Show file tree
Hide file tree
Showing 42 changed files with 4,519 additions and 43 deletions.
23 changes: 22 additions & 1 deletion src/EFCore.Design/Design/Internal/CSharpHelper.cs
Expand Up @@ -1001,7 +1001,28 @@ public virtual string Fragment(MethodCallCodeFragment fragment)
}

private string Fragment(NestedClosureCodeFragment fragment)
=> fragment.Parameter + " => " + fragment.Parameter + Fragment(fragment.MethodCall);
{
if (fragment.MethodCalls.Count == 1)
{
return fragment.Parameter + " => " + fragment.Parameter + Fragment(fragment.MethodCalls[0]);
}

var builder = new IndentedStringBuilder();
builder.AppendLine(fragment.Parameter + " =>");
builder.AppendLine("{");
using (builder.Indent())
{
foreach (var methodCall in fragment.MethodCalls)
{
builder.Append(fragment.Parameter + Fragment(methodCall));
builder.AppendLine(";");
}
}

builder.AppendLine("}");

return builder.ToString();
}

private static bool IsIdentifierStartCharacter(char ch)
{
Expand Down
Expand Up @@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.EntityFrameworkCore.Metadata;
Expand All @@ -29,6 +28,21 @@ public static class RelationalEntityTypeBuilderExtensions
string? name)
=> entityTypeBuilder.ToTable(name, (string?)null);

/// <summary>
/// Configures the table that the entity type maps to when targeting a relational database.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type being configured. </param>
/// <param name="buildAction"> An action that performs configuration of the table. </param>
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
public static EntityTypeBuilder ToTable(
this EntityTypeBuilder entityTypeBuilder,
Action<TableBuilder> buildAction)
{
buildAction(new TableBuilder(null, null, entityTypeBuilder));

return entityTypeBuilder;
}

/// <summary>
/// Configures the table that the entity type maps to when targeting a relational database.
/// </summary>
Expand All @@ -55,6 +69,23 @@ public static class RelationalEntityTypeBuilderExtensions
where TEntity : class
=> entityTypeBuilder.ToTable(name, (string?)null);

/// <summary>
/// Configures the table that the entity type maps to when targeting a relational database.
/// </summary>
/// <typeparam name="TEntity"> The entity type being configured. </typeparam>
/// <param name="entityTypeBuilder"> The builder for the entity type being configured. </param>
/// <param name="buildAction"> An action that performs configuration of the table. </param>
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
public static EntityTypeBuilder<TEntity> ToTable<TEntity>(
this EntityTypeBuilder<TEntity> entityTypeBuilder,
Action<TableBuilder<TEntity>> buildAction)
where TEntity : class
{
buildAction(new TableBuilder<TEntity>(null, null, entityTypeBuilder));

return entityTypeBuilder;
}

/// <summary>
/// Configures the table that the entity type maps to when targeting a relational database.
/// </summary>
Expand Down Expand Up @@ -119,7 +150,7 @@ public static class RelationalEntityTypeBuilderExtensions
Check.NotNull(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));

buildAction(new TableBuilder(name, schema, entityTypeBuilder.Metadata));
buildAction(new TableBuilder(name, schema, entityTypeBuilder));
entityTypeBuilder.Metadata.SetTableName(name);
entityTypeBuilder.Metadata.SetSchema(schema);

Expand Down Expand Up @@ -181,7 +212,7 @@ public static class RelationalEntityTypeBuilderExtensions
Check.NotNull(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));

buildAction(new TableBuilder<TEntity>(name, schema, entityTypeBuilder.Metadata));
buildAction(new TableBuilder<TEntity>(name, schema, entityTypeBuilder));
entityTypeBuilder.Metadata.SetTableName(name);
entityTypeBuilder.Metadata.SetSchema(schema);

Expand Down
12 changes: 8 additions & 4 deletions src/EFCore.Relational/Metadata/Builders/TableBuilder.cs
Expand Up @@ -22,12 +22,15 @@ public class TableBuilder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
public TableBuilder(string name, string? schema, IMutableEntityType entityType)
public TableBuilder(string? name, string? schema, EntityTypeBuilder entityTypeBuilder)
{
EntityType = entityType;
EntityTypeBuilder = entityTypeBuilder;
}

private IMutableEntityType EntityType { [DebuggerStepThrough] get; }
/// <summary>
/// The entity type builder for the entity being configured.
/// </summary>
public virtual EntityTypeBuilder EntityTypeBuilder { get; }

/// <summary>
/// Configures the table to be ignored by migrations.
Expand All @@ -36,7 +39,8 @@ public TableBuilder(string name, string? schema, IMutableEntityType entityType)
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
public virtual TableBuilder ExcludeFromMigrations(bool excluded = true)
{
EntityType.SetIsTableExcludedFromMigrations(excluded);
EntityTypeBuilder.Metadata.SetIsTableExcludedFromMigrations(excluded);

return this;
}

Expand Down
13 changes: 11 additions & 2 deletions src/EFCore.Relational/Metadata/Builders/TableBuilder`.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;

namespace Microsoft.EntityFrameworkCore.Metadata.Builders
Expand All @@ -22,11 +23,19 @@ public class TableBuilder<TEntity> : TableBuilder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
public TableBuilder(string name, string? schema, IMutableEntityType entityType)
: base(name, schema, entityType)
public TableBuilder(string? name, string? schema, EntityTypeBuilder<TEntity> entityTypeBuilder)
: base(name, schema, entityTypeBuilder)
{
}

/// <summary>
/// The entity type builder for the entity being configured.
/// </summary>
public new virtual EntityTypeBuilder<TEntity> EntityTypeBuilder
{
[DebuggerStepThrough] get => (EntityTypeBuilder<TEntity>)base.EntityTypeBuilder;
}

/// <summary>
/// Configures the table to be ignored by migrations.
/// </summary>
Expand Down
37 changes: 37 additions & 0 deletions src/EFCore.Relational/Migrations/IMigrationsAnnotationProvider.cs
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -101,5 +102,41 @@ public interface IMigrationsAnnotationProvider
/// <param name="checkConstraint"> The check constraint. </param>
/// <returns> The annotations. </returns>
IEnumerable<IAnnotation> ForRemove(ICheckConstraint checkConstraint);

/// <summary>
/// Gets provider-specific Migrations annotations for the given <see cref="ITable" />
/// when it is being renamed.
/// </summary>
/// <param name="table"> The table. </param>
/// <returns> The annotations. </returns>
IEnumerable<IAnnotation> ForRename(ITable table)
=> Enumerable.Empty<IAnnotation>();

/// <summary>
/// Gets provider-specific Migrations annotations for the given <see cref="IColumn" />
/// when it is being renamed.
/// </summary>
/// <param name="column"> The column. </param>
/// <returns> The annotations. </returns>
IEnumerable<IAnnotation> ForRename(IColumn column)
=> Enumerable.Empty<IAnnotation>();

/// <summary>
/// Gets provider-specific Migrations annotations for the given <see cref="ITableIndex" />
/// when it is being renamed.
/// </summary>
/// <param name="index"> The index. </param>
/// <returns> The annotations. </returns>
IEnumerable<IAnnotation> ForRename(ITableIndex index)
=> Enumerable.Empty<IAnnotation>();

/// <summary>
/// Gets provider-specific Migrations annotations for the given <see cref="ISequence" />
/// when it is being renamed.
/// </summary>
/// <param name="sequence"> The sequence. </param>
/// <returns> The annotations. </returns>
IEnumerable<IAnnotation> ForRename(ISequence sequence)
=> Enumerable.Empty<IAnnotation>();
}
}
25 changes: 20 additions & 5 deletions src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
Expand Up @@ -429,7 +429,6 @@ public virtual IReadOnlyList<MigrationOperation> GetDifferences(IRelationalModel
IRelationalModel? target)
{
var targetMigrationsAnnotations = target?.GetAnnotations().ToList();

if (source == null)
{
if (targetMigrationsAnnotations?.Count > 0)
Expand Down Expand Up @@ -603,13 +602,17 @@ protected virtual IEnumerable<MigrationOperation> Remove(string source, DiffCont
if (source.Schema != target.Schema
|| source.Name != target.Name)
{
yield return new RenameTableOperation
var renameTableOperation = new RenameTableOperation
{
Schema = source.Schema,
Name = source.Name,
NewSchema = target.Schema,
NewName = target.Name
};

renameTableOperation.AddAnnotations(MigrationsAnnotations.ForRename(source));

yield return renameTableOperation;
}

var sourceMigrationsAnnotations = source.GetAnnotations();
Expand Down Expand Up @@ -983,13 +986,17 @@ private static bool EntityTypePathEquals(IEntityType source, IEntityType target,

if (source.Name != target.Name)
{
yield return new RenameColumnOperation
var renameColumnOperation = new RenameColumnOperation
{
Schema = table.Schema,
Table = table.Name,
Name = source.Name,
NewName = target.Name
};

renameColumnOperation.AddAnnotations(MigrationsAnnotations.ForRename(source));

yield return renameColumnOperation;
}

var sourceTypeMapping = sourceMapping.TypeMapping;
Expand Down Expand Up @@ -1400,13 +1407,17 @@ private bool IndexStructureEquals(ITableIndex source, ITableIndex target, DiffCo

if (sourceName != targetName)
{
yield return new RenameIndexOperation
var renameIndexOperation = new RenameIndexOperation
{
Schema = targetTable.Schema,
Table = targetTable.Name,
Name = sourceName,
NewName = targetName
};

renameIndexOperation.AddAnnotations(MigrationsAnnotations.ForRename(source));

yield return renameIndexOperation;
}
}

Expand Down Expand Up @@ -1550,13 +1561,17 @@ protected virtual IEnumerable<MigrationOperation> Remove(ICheckConstraint source
if (source.Schema != target.Schema
|| source.Name != target.Name)
{
yield return new RenameSequenceOperation
var renameSequenceOperation = new RenameSequenceOperation
{
Schema = source.Schema,
Name = source.Name,
NewSchema = target.Schema,
NewName = target.Name
};

renameSequenceOperation.AddAnnotations(MigrationsAnnotations.ForRename(source));

yield return renameSequenceOperation;
}

if (source.StartValue != target.StartValue)
Expand Down
1 change: 1 addition & 0 deletions src/EFCore.Relational/Migrations/MigrationBuilder.cs
Expand Up @@ -1066,6 +1066,7 @@ public MigrationBuilder(string? activeProvider)
Name = name,
NewName = newName
};

Operations.Add(operation);

return new OperationBuilder<RenameIndexOperation>(operation);
Expand Down
16 changes: 16 additions & 0 deletions src/EFCore.Relational/Migrations/MigrationsAnnotationProvider.cs
Expand Up @@ -71,5 +71,21 @@ public virtual IEnumerable<IAnnotation> ForRemove(ISequence sequence)
/// <inheritdoc />
public virtual IEnumerable<IAnnotation> ForRemove(ICheckConstraint checkConstraint)
=> Enumerable.Empty<IAnnotation>();

/// <inheritdoc />
public virtual IEnumerable<IAnnotation> ForRename(ITable table)
=> Enumerable.Empty<IAnnotation>();

/// <inheritdoc />
public virtual IEnumerable<IAnnotation> ForRename(IColumn column)
=> Enumerable.Empty<IAnnotation>();

/// <inheritdoc />
public virtual IEnumerable<IAnnotation> ForRename(ITableIndex index)
=> Enumerable.Empty<IAnnotation>();

/// <inheritdoc />
public virtual IEnumerable<IAnnotation> ForRename(ISequence sequence)
=> Enumerable.Empty<IAnnotation>();
}
}
Expand Up @@ -50,6 +50,7 @@ public static AddPrimaryKeyOperation CreateFrom(IPrimaryKeyConstraint primaryKey
Name = primaryKey.Name,
Columns = primaryKey.Columns.Select(c => c.Name).ToArray()
};

operation.AddAnnotations(primaryKey.GetAnnotations());

return operation;
Expand Down

0 comments on commit e7c0b9d

Please sign in to comment.