首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >有没有扩展Code-First迁移的好方法

有没有扩展Code-First迁移的好方法
EN

Stack Overflow用户
提问于 2012-09-14 12:30:17
回答 3查看 3.1K关注 0票数 11

我正在开始一个使用Entity Framework的新项目。我研究了如何创建数据库的选项,发现代码优先迁移最有意义(如果您需要了解原因,请参阅底部)。代码优先迁移让我可以使用任意的SQL,这意味着我仍然拥有完全的控制权。在实践中,我发现这个问题是,对于一些常见的任务来说,使用SQL似乎是非常重复的。

就我的目的而言,我并不关心迁移中的扩展是否与提供者无关(我所在的SQL并非如此)。然而,我并没有在迁移框架中找到一个好的seam或扩展点来添加这样的东西。

举一个具体的例子,假设我想为MS-SQL复制指定一个RowGuid列。每一次发生都是以

代码语言:javascript
复制
Sql(
    string.Format(
        "Alter Table {0} Alter Column {1} Add ROWGUIDCOL",
        table,
        column ));

因此,我编写了静态方法来消除一些冗余

代码语言:javascript
复制
Sql( MigrationHelper.SetRowGuid( table, column );

-或者-

代码语言:javascript
复制
MigrationHelper.SetRowGuid(Sql, table, column); //passing the Sql method

可以在DbMigration上使用这些扩展方法中的任何一个,并通过this.访问它们,但这看起来仍然不合时宜:

代码语言:javascript
复制
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", "''" ):

这并不可怕,但对我来说,它仍然像是一个黑客。我必须重复表名,并且需要确保生成的列名是正确的。我发现表名需要重复很多次,但是列也是如此。然而,我真正想做的是在表名和列名都已知的情况下添加到表声明中。

然而,我找不到一个好的扩展点来扩展fluent接口,或者以一种感觉一致的方式扩展代码优先迁移。我是不是遗漏了什么?有没有人找到一种好的方法来做这件事?

关于我为什么会处于这种情况的一些理由:

出于几个原因,我不喜欢使用通用自定义属性解决方案来指示非映射数据库的常见解决方案,但最强烈的原因是它们不会被迁移自动拾取,这意味着额外的维护。Model-first解决方案之所以被淘汰,是因为它们不能完全控制数据库。Database-First很有吸引力,因为它的控件;但是,它没有Code-First Migrations提供的开箱即用的更改管理功能。因此,代码优先迁移似乎是赢家,因为代码优先模型驱动的更改是自动的,这意味着只有一件事需要维护。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-09-14 21:23:06

我已经找到了一个解决方案,尽管我不确定它是否好。我不得不走得比我想要的更远一点,它并不是一个真正的扩展点。

它允许我编写如下语句:

代码语言:javascript
复制
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)
      //SqlValue is a custom static helper class
    .DefaultConstraint( t => t.Description, SqlValue.EmptyString)
      //This is a convention in the project
      //Equivalent to
      //  .DefaultConstraint( t => t.RowGuid, SqlValue.EmptyString)
      //  .RowGuid( t => t.RowGuid )
    .StandardRowGuid()
      //For one-offs
    .Sql( tableName => string.Format( "ALTER TABLE {0} ...", tableName" );

我不喜欢:

如果使用了列定义的“”可选参数,则用于选择列的lambda可能会返回错误的列名。

我只是考虑在这里使用它,因为:

  • 我们发布了EF程序集,因此我们确信所使用的程序集将具有这些成员。
  • 两个单元测试将告诉我们新版本是否会破坏这些成员。
  • 它被隔离到migrations.
  • We,因此,如果新版本确实破坏了这些成员,我们可以设置一个黑客来替换它

代码语言:javascript
复制
internal static class TableBuilderExtentions
{
    internal static TableBuilder<TColumns> Sql<TColumns>(
        this TableBuilder<TColumns> tableBuilder,
        Func<string, string> sql,
        bool suppressTransaction = false,
        object anonymousArguments = null)
    {
        string sqlStatement = sql(tableBuilder.GetTableName());

        DbMigration dbMigration = tableBuilder.GetDbMigration();
        Action<string, bool, object> executeSql = dbMigration.GetSqlMethod();

        executeSql(sqlStatement, suppressTransaction, anonymousArguments);

        return tableBuilder;
    }

    [Pure]
    private static DbMigration GetDbMigration<TColumns>(this TableBuilder<TColumns> tableBuilder)
    {
        var field = tableBuilder.GetType().GetField(
            "_migration", BindingFlags.NonPublic | BindingFlags.Instance);
        return (DbMigration)field.GetValue(tableBuilder);
    }

    /// <summary>
    ///   Caution: This implementation only works on single properties.
    ///   Also, coder may have specified the 'name' parameter which would make this invalid.
    /// </summary>
    private static string GetPropertyName<TColumns>(Expression<Func<TColumns, object>> someObject)
    {
        MemberExpression e = (MemberExpression)someObject.Body;

        return e.Member.Name;
    }

    [Pure]
    private static Action<string, bool, object> GetSqlMethod(this DbMigration migration)
    {
        MethodInfo methodInfo = typeof(DbMigration).GetMethod(
            "Sql", BindingFlags.NonPublic | BindingFlags.Instance);
        return (s, b, arg3) => methodInfo.Invoke(migration, new[] { s, b, arg3 });
    }

    [Pure]
    private static string GetTableName<TColumns>(this TableBuilder<TColumns> tableBuilder)
    {
        var field = tableBuilder.GetType().GetField(
            "_createTableOperation", BindingFlags.NonPublic | BindingFlags.Instance);

        var createTableOperation = (CreateTableOperation)field.GetValue(tableBuilder);
        return createTableOperation.Name;
    }
}
票数 7
EN

Stack Overflow用户

发布于 2019-05-17 01:52:07

要利用ravi所说的内容,可以扩展DbMigration类:

代码语言:javascript
复制
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 **");
    }
}

然后,在创建迁移时,将其从DbMigration更改为ExtendedDbMigration

代码语言:javascript
复制
using System.Data.Entity.Migrations;

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

    public override void Down()
    {
        UndoCommonTask("Down");
    }
}
票数 1
EN

Stack Overflow用户

发布于 2012-09-14 14:25:44

不是通用的解决方案,但您可以从抽象/接口类继承。考虑到这一点,需要对代码进行一些修改,但它相当干净。

我已经使用此模式为所有表定义了我的审计列(UpdatedBy、UpdateDate等)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12418144

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档