Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a good way to extend the Code-First Migrations

I am starting a new project that uses Entity Framework. I have researched my options for how to create the database and found Code-First Migrations make the most sense (see bottom if you need to know why). Code-First Migrations lets me drop down to arbitrary SQL meaning I still have full control. In practice I found that issue is that dropping down to SQL seems terribly repetitive for some of the common tasks.

For my purposes, I do not care that extensions in the migration be provider agnostic (the SQL I am in-lining is not). However, I am not really finding a good seam or extension point in the migrations framework for adding such things.

To give a concrete example, suppose I want to specify a RowGuid column for MS-SQL replication. Every occurance takes the form of

Sql(
    string.Format(
        "Alter Table {0} Alter Column {1} Add ROWGUIDCOL",
        table,
        column ));

So I write static methods to get rid of some of the redundancy

Sql( MigrationHelper.SetRowGuid( table, column );

-or-

MigrationHelper.SetRowGuid(Sql, table, column); //passing the Sql method

Possibly could make either of those extension methods on DbMigration, and access them by this. But still this looks out of place:

CreateTable(
    "dbo.CustomerDirectory",
     c => new
         {
             Uid = c.Int(nullable: false),
             CustomerUid = c.Int(nullable: false),
             Description = c.String(nullable: false, maxLength: 50, unicode: false),
             RowGuid = c.Guid(nullable: false),
         })
     .PrimaryKey(t => t.Uid)
     .ForeignKey("dbo.Customer", t => t.CustomerUid);

this.SetRowGuid( Sql, "dbo.CustomerDirectory", "RowGuid" );
//Custom method here because of desired naming convention of Constraint
this.SetDefaultConstraint( Sql, "dbo.CustomerDirectory", "''" ):

It is not terribly, but it still feels like a hack to me. I have I have to repeat the table name, and I need to make sure I get the generated column name right. I find the table name needs to be repeated a lot, but so do columns. Yet what I am really trying to do as add onto the table declaration that just happened where both table name and column names were all known.

I, however, could not find a good extension point for extending the fluent interface or otherwise extending the code first migrations in a way that feels consistent. Am I missing something? Has anyone founds a good way of doing this?

Some justification as to why I am in this situation:

I did not like the what seemed like a common solution of using common custom attributes solution to indicate non-mapping database for a few reasons, but most strongly because they are not automatically picked up by migrations meaning extra maintenance. Model-first solutions were out out because they does not give full control over the database. Database-First was appealing because of the control; however, it does does not have the out-of-the-box change management functionality Code-First Migrations provides. Thus, Code-First Migrations seemed to be a winner because [code-first] model-driven changes are automatic and it meant there would be only one thing to maintain.

like image 318
vossad01 Avatar asked Sep 14 '12 04:09

vossad01


1 Answers

To piggyback on what ravi said, you could extend the DbMigration class:

using System;
using System.Collections.Generic;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

public abstract class ExtendedDbMigration : DbMigration
{
    public void DoCommonTask(string parameter)
    {
        Sql("** DO SOMETHING HERE **");
    }

    public void UndoCommonTask(string parameter)
    {
        Sql("** DO SOMETHING HERE **");
    }
}

Then, when you create a migration, change it from DbMigration to ExtendedDbMigration:

using System.Data.Entity.Migrations;

public partial class some_migration : ExtendedDbMigration
{
    public override void Up()
    {
        DoCommonTask("Up");
    }

    public override void Down()
    {
        UndoCommonTask("Down");
    }
}
like image 82
dochoffiday Avatar answered Sep 23 '22 23:09

dochoffiday