How to add custom Add-Migration behaviour in EF Core?

c# entity-framework entity-framework-core

Question

My goal is to create a custom Attribute and allow. Add-Migration to have custom code generated based on it.

The Model and Attribute class

public class MyAttribute: Attribute {}

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

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

The MigrationProvider and AnnotationProvider:

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>();
    }
}

Generated Migration codes ( with some clean up )

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" );
}

As you can see the MyAttribute annotation is successfully added to the Up method. However, I couldn't seem to override the Generate method since there's no Hello world exception throw while running Add-Migration.

I am using EF Core 1.1.5

Thanks in advance!

1
4
12/19/2017 3:01:28 AM

Popular Answer

The IMigrationsSqlGenerator can only process a MigrationOperation that has been generated. To detect changes in your new Attribute, you will probably need to replace the IMigrationsModelDiffer service. Then you can return a new SqlOperation (or custom type) with the other differences between the two models.

On the plus side, this means that you can generate undo operations in the Down process too.

0
9/17/2018 12:59:43 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow