Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 786
user1510539 Avatar asked Dec 19 '17 03:12

user1510539


People also ask

How do I add my first migration to EF Core?

You're now ready to add your first migration! Instruct EF Core to create a migration named InitialCreate: EF Core will create a directory called Migrations in your project, and generate some files. It's a good idea to inspect what exactly EF Core generated - and possibly amend it - but we'll skip over that for now.

What is Entity Framework Core migration?

Migration in Entity Framework Core Migration is a way to keep the database schema in sync with the EF Core model by preserving data. As per the above figure, EF Core API builds the EF Core model from the domain (entity) classes and EF Core migrations will create or update the database schema based on the EF Core model.

How do I create a migration using DotNet EF?

If you are using dotnet Command Line Interface, execute the following command. CLI. > dotnet ef migrations add MyFirstMigration. In the above commands, MyFirstMigration is the name of a migration. This will create three files in the Migrations folder of your project, as shown below.

How to create a custom migration for a customer class?

If you go ahead and run dotnet ef migrations add Initial it'll create a migration for your Customer class. Running dotnet ef database update will create the Db and you'll be able to see the Customer table. This is where I intend to put all of the custom migrations for an application.


1 Answers

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.

like image 115
Jeremy Lakeman Avatar answered Oct 05 '22 01:10

Jeremy Lakeman