C# 如何首先在EF核心代码中定制迁移生成?

C# 如何首先在EF核心代码中定制迁移生成?,c#,sql-server,asp.net-core,entity-framework-core,entity-framework-migrations,C#,Sql Server,Asp.net Core,Entity Framework Core,Entity Framework Migrations,我的DbContext中有一种特殊的基表类型。当从它继承时,我需要生成一个额外的“SQL”迁移操作来为它创建一个特定的触发器。它通过检查重叠范围来确保表结构的一致性。因为SQL Server中没有重叠的索引或检查约束,所以我必须使用触发器(在检查约束中使用函数会导致迁移出现同样的问题,同时SQL中的函数“名称空间”也会很混乱) 因为我还没有找到任何方法在建模过程中创建触发器,所以我想改变生成的迁移。但如何做到这一点呢 尝试使用SQLServerMigrationSqlGenerator和SQLS

我的
DbContext
中有一种特殊的基表类型。当从它继承时,我需要生成一个额外的“SQL”迁移操作来为它创建一个特定的触发器。它通过检查重叠范围来确保表结构的一致性。因为SQL Server中没有重叠的索引或检查约束,所以我必须使用触发器(在检查约束中使用函数会导致迁移出现同样的问题,同时SQL中的函数“名称空间”也会很混乱)

因为我还没有找到任何方法在建模过程中创建触发器,所以我想改变生成的迁移。但如何做到这一点呢

尝试使用
SQLServerMigrationSqlGenerator
SQLServerMigrationAnnotationProvider
,但顾名思义,它们仅在生成SQL命令的最后阶段使用。这使得它们在使用迁移时有点“隐藏”在视线之外。在需要时很难定制并在以后进行维护

考虑使用
CSharpMigrationOperationGenerator
,它似乎非常适合我的需要。但是有一个问题-我不能访问这个类。也不是名称空间

根据源代码,该类位于Microsoft.EntityFrameworkCore.Migrations.Design命名空间中,是公共的。为了访问它,必须安装
Microsoft.EntityFrameworkCore.Design
软件包

但它不起作用


我错过了什么?如何访问和继承这个类?或者,有一种更好、更合适的方法可以在迁移过程中为特定表自动创建触发器?

打开迁移文件并更改
Up
方法

然后从package manager控制台使用更新数据库应用迁移

大概是这样的:

public partial class CreateDatabase : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("Some custom SQL statement");
        migrationBuilder.CreateTable(
            name: "Authors",
            columns: table => new
            {
                AuthorId = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                FirstName = table.Column<string>(nullable: true),
                LastName = table.Column<string>(nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Authors", x => x.AuthorId);
            });
    }
}
公共部分类CreateDatabase:迁移
{
受保护的覆盖作废(MigrationBuilder MigrationBuilder)
{
Sql(“一些自定义Sql语句”);
migrationBuilder.CreateTable(
姓名:“作者”,
列:表=>new
{
AuthorId=table.Column(可空:false)
.Annotation(“SqlServer:ValueGenerationStrategy”,SqlServerValueGenerationStrategy.IdentityColumn),
FirstName=table.Column(可空:true),
LastName=table.Column(可空:true)
},
约束:表=>
{
表.PrimaryKey(“PK_Authors”,x=>x.authord);
});
}
}
如何提供自己的
ICSharpMigrationOperationGenerator
实现 考虑使用CSharpMigrationOperationGenerator,它似乎非常适合我的需要。但是有一个问题-我不能访问这个类。也不是名称空间

根据源代码,此类位于Microsoft.EntityFrameworkCore.Migrations.Design命名空间中,是公共的。为了访问它,必须安装Microsoft.EntityFrameworkCore.Design软件包

但它不起作用

我错过了什么如何访问和继承此类?

假设您在设计时调用以下CLI命令来添加新迁移:

dotnet ef migrations add "SomeMigration"
下面是一个完全工作的示例控制台程序,它将使用一个自定义的
ICSharpMigrationOperationGenerator
实现,名为
mycsharrpmigrationoperationgenerator
,它继承自
csharrpmigrationoperationgenerator

使用系统;
使用Microsoft.EntityFrameworkCore;
使用Microsoft.EntityFrameworkCore.Design;
使用Microsoft.EntityFrameworkCore.Internal;
使用Microsoft.EntityFrameworkCore.Migrations.Design;
使用Microsoft.EntityFrameworkCore.Migrations.Operations;
使用Microsoft.Extensions.DependencyInjection;
使用Microsoft.Extensions.Logging;
命名空间IssueConsoleTemplate
{
公共类MyCSharpMigrationOperationGenerator:CSharpMigrationOperationGenerator
{
公共MyCSharpMigrationOperationGenerator(CSharpMigrationOperationGeneratorDependencies依赖项)
:base(依赖项)
{
}
受保护的覆盖无效生成(CreateTableOperation操作,缩进StringBuilder)
{
Console.WriteLine(“\r\n\r\n--\r\nMySharpMigrationOperationGenerator被使用\r\n--\r\n”);
生成(操作、生成器);
}
}
公共类MyDesignTimeServices:IDesignTimeServices
{
public void配置DesignTimeServices(iSeries收集服务)
=>services.AddSingleton();
}
公共级冰淇淋
{
public int IceCreamId{get;set;}
公共字符串名称{get;set;}
}
公共类上下文:DbContext
{
公共DbSet冰淇淋{get;set;}
配置时受保护的覆盖无效(DBContextOptions Builder Options Builder)
{
选项生成器
.UseSqlServer(@“数据源=。\MSSQL14;集成安全性=SSPI;初始目录=So63575132”)
.UseLoggerFactory(
LoggerFactory,创建(
b=>b
.AddConsole()
.AddFilter(level=>level>=LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}
内部静态类程序
{
私有静态void Main()
{
}
}
}
MyCSharpMigrationOperationGenerator
类为每个添加的表输出以下行,以证明它被调用:

---
使用MyCSharpMigrationOperationGenerator
---
正如@KasbolatKumakhov在他的评论中指出的那样,还应该注意从2.2中引用
Microsoft.EntityFrameworkCore.Design
的方式。T
public class MyMigrationsModelDiffer : MigrationsModelDiffer {

  public MyMigrationsModelDiffer(IRelationalTypeMappingSource typeMappingSource,
    IMigrationsAnnotationProvider migrationsAnnotations,
    IChangeDetector changeDetector,
    IUpdateAdapterFactory updateAdapterFactory,
    CommandBatchPreparerDependencies commandBatchPreparerDependencies)
    : base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies) { }

  protected override IEnumerable<MigrationOperation> Diff(IModel source, IModel target, DiffContext diffContext) {
    return base.Diff(source, target, diffContext).Concat(GetTriggerTriggerDifferences(source, target));
  }

  public override Boolean HasDifferences(IModel source, IModel target) {
    return base.HasDifferences(source, target) || HasTriggerAnnotationDifferences(source, target);
  }

  public IEnumerable<MigrationOperation> GetTriggerTriggerDifferences(IModel source, IModel target) {
    if (source == null || target == null) {
      return new new List<MigrationOperation>(0);
    }

    Dictionary<String, IAnnotation> triggerAnnotationPerEntity = new Dictionary<String, IAnnotation>();
    foreach (var entityType in source.GetEntityTypes()) {
      triggerAnnotationPerEntity[entityType.Name] = GetTableAnnotation(entityType);
    }
    var operations = new List<MigrationOperation>();
    foreach (var entityType in target.GetEntityTypes()) {
      triggerAnnotationPerEntity.TryGetValue(entityType.Name, out IAnnotation sourceTriggerTable);
      IAnnotation targetTriggerTable = GetTableAnnotation(entityType);

      if (targetTriggerTable?.Value == sourceTriggerTable?.Value) {
        continue;
      }

      Boolean isCreate = targetTriggerTable != null;
      String tableName = (entityType as EntityType)?.GetTableName();
      String primaryKey = entityType.FindPrimaryKey().Properties[0].Name;
      if (isCreate) {
        SqlOperation sqlOperation = new SqlOperation();
        sqlOperation.Sql = $@"CREATE TRIGGER...";
        operations.Add(sqlOperation);
      }
      else {
        // drop trigger sqloperation
      }
    }
    return operations;
  }

  private static IAnnotation GetTableAnnotation(IEntityType entityType) {
    return entityType.GetAnnotations()?.FirstOrDefault(x => x.Name == "WantTrigger");
  }

  public Boolean HasTriggerAnnotationDifferences(IModel source, IModel target) {
    return GetTriggerTriggerDifferences(source, target).Any();
  }
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
  base.OnConfiguring(optionsBuilder);
  if (optionsBuilder == null) {
    return;
  }
  optionsBuilder.ReplaceService<IMigrationsModelDiffer, MyMigrationsModelDiffer>();
}
builder.Entity<MyTable>().HasAnnotation("WantTrigger", "1.0");
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;

public static class MigrationBuilderExtensions
{
    public static void ConfigForOracle(this MigrationBuilder migrationBuilder)
    {
        //For each table registered in the builder, let's create a sequence and a trigger
        foreach (CreateTableOperation createTableOperation in migrationBuilder.Operations.ToArray().OfType<CreateTableOperation>())
        {
            string tableName = createTableOperation.Name;
            string primaryKey = createTableOperation.PrimaryKey.Columns[0];
            migrationBuilder.CreateSequence<int>(name: $"SQ_{tableName}", schema: createTableOperation.Schema);
            migrationBuilder.Sql($@"CREATE OR REPLACE TRIGGER ""TR_{tableName}""
                                    BEFORE INSERT ON ""{tableName}""
                                    FOR EACH ROW
                                    WHEN (new.""{primaryKey}"" IS NULL)
                                    BEGIN
                                        SELECT ""SQ_{tableName}"".NEXTVAL
                                        INTO   :new.""{primaryKey}""
                                        FROM   dual;
                                    END;");
        }
    }
}