【问题标题】:How to add custom Add-Migration behaviour in EF Core?如何在 EF Core 中添加自定义添加迁移行为?
【发布时间】:2017-12-19 03:01:28
【问题描述】:

我的目标是创建一个自定义 Attribute 并允许。 Add-Migration 基于它生成自定义代码。

模型和Attribute

public class MyAttribute: Attribute {}

public class MyModel
{
    public int Id { get; set; }

    [MyAttribute]
    public string Name { get; set; }
}

MigrationProviderAnnotationProvider

internal class MyMigrationsAnnotationProvider : SqliteMigrationsAnnotationProvider
{
    public override IEnumerable<IAnnotation> For( IProperty property )
    {
        MemberInfo MInfo = property.PropertyInfo ?? ( MemberInfo ) property.FieldInfo;
        MyAttribute MyAttr = MInfo?.GetCustomAttribute<MyAttribute>();

        if ( MyAttr != null )
        {
            return base.For( property ).Concat( new IAnnotation[] { new Annotation( "MyAttribute", true ) } );
        }

        return base.For( property );
    }
}

internal class MyMigrationsSqlGenerator : SqliteMigrationsSqlGenerator 
{
    public MyMigrationsSqlGenerator( IRelationalCommandBuilderFactory IRFactory, ISqlGenerationHelper ISHelper,  IRelationalTypeMapper Mapper, IRelationalAnnotationProvider AnProvider )
        : base( IRFactory, ISHelper, Mapper, AnProvider ) {}

    protected override void Generate( AddColumnOperation operation, IModel model, MigrationCommandListBuilder builder )
    {
        throw new Exception( "Hello world" );
        // Here's where I got it wrong.
        // I thought I should be able to read the "MyAttribute" annotation from here and generate extra code in the Up method
        /*
        if( operation.FindAnnotation( "MyAttribute" ) != null )
        {
            builder.AppendLine( "Hello there, not sure if this would work." );
        }
        */
    }
}

class MyContext : DbContext
{
    public DbSet<MyModel> MModel { get; set; }

    protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder )
    {
        optionsBuilder.UseSqlite( "Data Source=mydata.db" );
        optionsBuilder.ReplaceService<IMigrationsSqlGenerator, MyMigrationsSqlGenerator>();
        optionsBuilder.ReplaceService<IMigrationsAnnotationProvider, MyMigrationsAnnotationProvider>();
    }
}

生成的迁移代码(经过一些清理)

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateTable(
        name: "MyModel",
        columns: table => new
        {
            Id = table.Column<string>(nullable: false),
            Name = table.Column<string>(nullable: false)
                .Annotation("MyAttribute", true),
        });
    // The following line is what I want it to be generated
    migrationBuilder.Sql( "I WANT MY CUSTOM QUERY BE GENERATED HERE" );
}

protected override void Down(MigrationBuilder migrationBuilder)
{
    migrationBuilder.DropTable( name: "MyModel" );
    // The following line is what I want it to be generated
    migrationBuilder.Sql( "I WANT MY CUSTOM QUERY BE GENERATED HERE" );
}

如您所见,MyAttribute 注释已成功添加到Up 方法中。但是,我似乎无法覆盖 Generate 方法,因为在运行 Add-Migration 时没有抛出 Hello world 异常。

我使用的是 EF Core 1.1.5

提前致谢!

【问题讨论】:

    标签: c# entity-framework entity-framework-core


    【解决方案1】:

    IMigrationsSqlGenerator 只能处理已生成的MigrationOperation。要检测新 Attribute 中的更改,您可能需要替换 IMigrationsModelDiffer 服务。然后可以返回一个新的 SqlOperation(或自定义类型),其中包含两个模型之间的其他差异。

    从好的方面来说,这意味着您也可以在 Down 过程中生成撤消操作。

    【讨论】:

    • 大概就是这个了。你能提供一些例子来摆弄吗?
    • 我已经使用这个技巧添加了额外的操作以在架构更改之前关闭审计跟踪,我不确定你需要什么来检测属性更改。
    • 只是一个展示如何拦截迁移 SQL 生成的示例对我来说已经足够了。我会从那里处理剩下的。谢谢!
    • 我只是覆盖了MigrationsModelDifferGetDifferences,检查了每个操作,如果我发现一个与我正在寻找的操作相匹配的操作,我会在返回的集合中插入其他操作。您可能希望重写用于比较 IProperty 的 Diff 方法,以便添加所需的操作。
    【解决方案2】:

    问题是AddColumnOperation 方法只有在现有表中添加新列时才会被调用。

    对于CreateTable,您需要改写void Generate(CreateTableOperation operation, IModel model, MigrationCommandListBuilder builder) 方法。 CreateTableOperation 包含相同 AddColumnOperation 类型的 Operations 属性。

    这是一个完整的例子

    protected override void Generate(CreateTableOperation operation, IModel model, MigrationCommandListBuilder builder)
    {
        base.Generate(operation, model, builder);
        foreach (var columnOperation in operation.Columns) //columnOperation is AddColumnOperation
        {
            //operation.FindAnnotation("MyAttribute")
        }
    }
    

    希望这会有所帮助!

    【讨论】:

    • 谢谢。然而,这不是我最初想要的。与您的类似,我找到了解决此问题的替代解决方案。但是我仍然想知道是否有一种方法可以自定义如何生成迁移代码的说明。所以我现在将这个问题保留为活跃状态。
    猜你喜欢
    • 2017-05-30
    • 2017-11-30
    • 2018-11-30
    • 1970-01-01
    • 1970-01-01
    • 2017-04-12
    • 2018-02-10
    • 1970-01-01
    • 2021-05-15
    相关资源
    最近更新 更多