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!
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With