EF6:编写你自己的code first 数据迁移操作(睡前来一篇,翻译的)

原英文版由EF团队成员 Rowan Miller 在2013年发表,此处只作翻译备忘。

数据迁移提供了一套强类型API,用于执行通用的操作,比如CreateIndex("dbo.Blogs","Url")。同时,也提供了在一些特殊的情况下用户需要执行特殊SQL的接口,比如Sql("Grant Select On dbo.Blogs to guest);。当然,这个SQL的接口也有一些缺点——那就是一旦你写了SQL那么就意谓着你的程序不再数据库无关了(比如ORACLE的语法和SQL SERVER有时候并不一样,这造成了不兼容)。

你可以把你需要的强类型API提交给ICECLOW。

创建我们自己的操作

我们打算添加一个允许我们将一张表的权限赋给一个用户的操作。正常情况下,我们希望写一个类似GrantPermission("dbo.Blogs","guest",Permission.Select);的方法。

首先,我们创建一个MigrationOperation的子类,即一个自定义操作。除了实现IsDestructiveChange这个属性别的不需要任何操作。

using System.Data.Entity.Migrations.Model;
 
namespace ExtendingMigrations.Migrations
{
  public enum Permission
  {
    Select,
    Update,
    Delete
  }
 
  public class GrantPermissionOperation : MigrationOperation
  {
    public GrantPermissionOperation(string table, string user, Permission permission)
      : base(null)
    {
      Table = table;
      User = user;
      Permission = permission;
    }
 
    public string Table { get; private set; }
    public string User { get; private set; }
    public Permission Permission { get; private set; }
 
    //是否为破坏性的修改
    public override bool IsDestructiveChange
    {
      get { return false; }
    }
  }
}

下面,我们来写一个扩展方法以实现我们想要的功能。我们使用IDbMigration接口,这个接口让我们可以有权限操作DbMigration中不可见的API。(比如下面migration,本身没有AddOperation操作【fuck,这是怎么实现的!】,但是,强制转换为IDbMigration的时候就有了)

using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
 
namespace ExtendingMigrations.Migrations
{
  public static class Extensions
  {
    public static void GrantPermission(this DbMigration migration, string table, string user, Permission permission)
    {
      ((IDbMigration)migration)
        .AddOperation(new GrantPermissionOperation(table, user, permission));
    }
  }
}

你可以把上面的扩展方法作为普通的方法写在GrantPermissionOperation类中,但是,假如像我这样使用了扩展方法,那么在类中我们就有更漂亮的写法。如下:

namespace ExtendingMigrations.Migrations
{
    using System;
    using System.Data.Entity.Migrations;
 
    public partial class GrantGuestPermissions : DbMigration
    {
        public override void Up()
        {
          this.GrantPermission("dbo.Blogs", "guest", Permission.Select);
        }
 
        public override void Down()
        {
        }
    }
}

为我们的操作创建SQL

如果我们现在就调试运行我们的新的数据迁移类,那么肯定会出现异常。因为默认的SQL生成器不知道我们的操作流程。不过,我们可以从已经存在的migrator(迁移器?可以这样叫么?)中继承然后将新的SQL生成逻辑添加到我们的操作中。

using System.Data.Entity.Migrations.Model;
using System.Data.Entity.Migrations.Sql;
 
namespace ExtendingMigrations.Migrations
{
  public class MySqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
  {
    protected override void Generate(MigrationOperation migrationOperation)
    {
      var operation = migrationOperation as GrantPermissionOperation;
      if (operation != null)
      {
        using (var writer = Writer())
        {
          writer.WriteLine(
            "GRANT {0} ON {1} TO {2}",
            operation.Permission.ToString().ToUpper(),
            operation.Table,
            operation.User);
 
          Statement(writer);
        }
      }
    }
  }
}

注意上面的代码中,Generate方法被我们重写了,但是,我们却没有执行base.Generate(..);方法,因为父类的方法不知道如何处理这个逻辑,所以即使调用也会报异常。而且,也只有在我们默认的SQL生成器不知道如何处理我们自定义SQL的时候才会调用我们的Generate(MigrationOperation)方法。

最后,我们需要在数据迁移配置(migrations configuration)中注册我们新的SQL生成器:

namespace ExtendingMigrations.Migrations
{
  using System.Data.Entity.Migrations;
 
  internal sealed class Configuration : DbMigrationsConfiguration<ExtendingMigrations.BloggingContext>
  {
    public Configuration()
    {
      AutomaticMigrationsEnabled = false;
 
      // Register our custom generator
      SetSqlGenerator("System.Data.SqlClient", new MySqlServerMigrationSqlGenerator());
    }
  }
}

测试

在我们VS的包管理控制台中输入Update-Database -Script,会输出更新数据库(对数据库进行数据迁移)时要执行的SQL脚本,其如下:

GRANT SELECT ON dbo.Blogs TO guest
 
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES ('201302272012532_GrantGuestPermissions', 'ExtendingMigrations.Migrations.Configuration', 0x1F8B0800000..., '6.0.0-alpha3-20222')

posted @ 2015-12-20 00:17  ensleep  阅读(1135)  评论(0编辑  收藏  举报