【问题标题】:How do I generate an EF6 database with migrations enabled, without using update-database?如何在不使用更新数据库的情况下生成启用迁移的 EF6 数据库?
【发布时间】:2014-09-18 00:56:57
【问题描述】:

在 EF5 中,我依赖于可以使用 Database.CreateIfNotExists() 从模型中重新创建数据库的事实

我会根据需要生成迁移,但从不将它们签入源代码控制(因为它们往往是开发周期的产物)然后每个开发人员都会根据需要从模型中删除并重新创建自己的数据库。

然后,我们将通过比较代码的分支来生成迁移,并获取 SQL,以应用于生产或其他共享数据库。

此工作流程似乎不再有效,因为在启用迁移时无法从头开始生成数据库,除非先生成所有迁移然后调用 update-database。由于调用 add-migration 会修改 csproj 文件,这使得我的脚本(允许我们轻松切换分支)无法使用。

已为上下文“ApplicationDbContext”启用迁移,但数据库不存在或不包含映射表。使用迁移来创建数据库及其表,例如通过从包管理器控制台运行“更新数据库”命令。

有什么方法可以恢复到 Database.Create 将创建当前版本的数据库的 EF5 行为?

【问题讨论】:

  • 目前似乎还没有明确的答案。我已将其添加为issue on CodePlex 如果您想修复它,请在那边投票。

标签: entity-framework


【解决方案1】:

我使用 CreateDatabaseIfNotExists 来初始化数据库,并且能够使用 update-database 并运行我的应用程序,如果数据库不存在,它将创建它。这似乎与 EF 6.0.x 不同。

This was useful, but not what I used.

我所做的是放弃我现有的初始化代码,并将其替换为 Global.asax 中的以下代码。

Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());
        using (MyContext temp = new MyContext())
        {
            temp.Database.Initialize(true);
        }

注意:MyContext 是我使用的上下文,Configuration 是启用迁移时创建的配置文件。

我已经看到很多关于人们对此有问题的帖子,但描述解决方案的帖子并不多。我不知道为什么这么多人,包括我自己,都错过了这个突破性的变化......(如果在某个地方有关于这个的描述,我从来没有看到它,直到为时已晚。)

编辑:

这是我添加到上下文类中的代码。我不像以前那样喜欢它,但它现在可以完成工作。请参阅 @Doug 对 OP 对 CodePlex 投票的评论。

private static readonly Object syncObj = new Object();
public static bool InitializeDatabase()
{
    lock (syncObj)
    {
        using (var temp = new TbdContext())
        {
            ObjectContext oc = null;
            try
            {
                oc = temp.ObjectContext;
            }
            catch (Exception ex)
            {
                //Ignore error
                Console.WriteLine(ex);
            }

            if (oc != null && oc.DatabaseExists())
            {
                return true;
            }
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<TbdContext, Configuration>());
            try
            {
                temp.Database.Initialize(true);
                return true;
            }
            catch (DataException ex)
            {

        }
    }
}

然后在我的 Global.asax.cs Application_Start() 我这样做:

if (databaseInitialized == false)
    databaseInitialized = MyContext.InitializeDatabase();

编辑:

我升级到 EF 6.1 后发现它不再起作用了。(https://stackoverflow.com/a/22770517/2033294) 以下是我修复它的方法:

    private static readonly Object syncObj = new Object();
    public static bool InitializeDatabase()
    {
        lock (syncObj)
        {
            using (var temp = new MyContext())
            {
                if (temp.Database.Exists()) return true;

                var initializer = new MigrateDatabaseToLatestVersion<MyContext, Configuration>();
                Database.SetInitializer(initializer);
                try
                {
                    temp.Database.Initialize(true);
                    return true;
                }
                catch (Exception ex)
                {
                    //Handle Error in some way
                    return false;
                }
            }
        }
    }

【讨论】:

  • 这样做的问题是它会将您的数据库从以前的版本迁移到新版本......它与 CreateDatabaseIfNotExists 不完全匹配,它仅在创建最新版本的数据库时不存在同名数据库。当我在数据库中有实际数据时,我希望对迁移进行更精细的控制
  • @dannykay1710,我不确定我是否关注你,但我愿意。两个问题。首先,您是什么意思它将迁移到新版本?这将运行数据库迁移你是说 CreateDatabaseIfNotExists 只会在数据库不存在的情况下运行迁移?其次,您想要/需要的精细控制示例是什么?
  • 我不喜欢我编译的应用程序会自动对我的实时数据库进行架构更改而没有警告,只是因为新版本有新的迁移。我只希望在配置的数据库不存在时自动创建架构——而不是每当有新的迁移时。架构更改很容易导致数据丢失。虽然您可以要求 EF 在发生数据丢失时不要进行更改,但在大多数情况下,我仍然希望使用控件来测试和控制此更改。
  • @dannykay1710,如果我错了,请纠正我...如果您进行更改需要新的迁移,则在运行迁移之前您将无法测试新代码正确的? (我不记得确切的措辞,但 EF 会抛出模型和数据库不匹配的异常)。所以在测试后的某个时候,您需要手动启用迁移或更新数据库,对吗?对于数据丢失,旧版本的 EF 是否有同样的问题?基于这些 cmets,我将研究如何使我的解决方案更加健壮。
  • @Jeff - 我的想法是我曾经从字面上删除数据库,并使用上述过程(曾经有效)重新创建它使用你的方法,我必须创建迁移(如你所说),然后运行您描述的代码(确实有效) 不同之处在于我不必为 EF5 中的干净数据库生成迁移。在 EF6 中,我必须创建迁移,更新我的数据库,然后删除它们。我宁愿只在需要进行生产迁移时创建迁移(不在我的日常开发工作流程中)
【解决方案2】:

这对我有用(到目前为止...):

var connectionString = "Data Source=localhost;Initial Catalog=Chilli;" + 
    "Integrated Security=True;" + 
    "MultipleActiveResultSets=True;Application Name=ChilliDesktop";

//ooops... you have to create a new, blank database if one does not exist
var bld = new SqlConnectionStringBuilder(connectionString);
var dbName = bld.InitialCatalog; //i.e. Chilli

bld.InitialCatalog = "master";

//a connectionstring pointing at Master
var masterConnectionString = bld.ConnectionString; 

using (var cnn = new SqlConnection(masterConnectionString))
{
  var cmdString = string.Format(
     "if not exists (select * from sys.databases where name='{0}')" + 
     "\r\ncreate database {0}",
     dbName);
   using (var cmd = new System.Data.SqlClient.SqlCommand(cmdString,cnn))
   {
     cmd.Connection.Open();
     cmd.ExecuteNonQuery();
   }                
 }


var connectionInfo = 
    new System.Data.Entity.Infrastructure
        .DbConnectionInfo(connectionString, "System.Data.SqlClient");
//new up your auto-created Migrations Configuration class
var config = new Chilli.Context.Migrations.Configuration();
config.TargetDatabase = connectionInfo;
//new up a Migrator
var migrator = new System.Data.Entity.Migrations
    .DbMigrator(config);
migrator.Update();
//you now have a database

//run your Seed method
using (var dc = new Chilli.Context.Context(connectionString))
{
    Chilli.Context.ChilliContextInitializerHelper.Seed(dc);
}

http://softwaremechanik.wordpress.com/2013/11/04/ef6-if-migrations-are-enabled-cannot-createdatabaseifnotexists/

【讨论】:

  • 刚刚意识到,您必须在调用 Update() 之前创建一个新的空白数据库
  • 嗨@dannykay1710:对于迁移的精细控制,获取待处理迁移列表,然后仅应用其中一些... var migrator = new System.Data.Entity.Migrations .DbMigrator(config); var pendingMigrations = migrator.GetPendingMigrations();
【解决方案3】:

这就是答案...

EF 团队分类:这是“按设计”,是我们在 EF6 中进行的有意更改。如果此初始化程序用于创建数据库,则将单个条目添加到 __MigrationsHistory 表中,然后使数据库无法用于迁移(因为这些初始化程序不使用您的迁移来创建数据库)。这在以前的版本中让很多人感到困惑,因此我们选择在启用迁移时不自动创建数据库/架构。

如果您只想应用迁移,可以使用 MigrateDatabaseToLatestVersion 初始化程序。如果要删除数据库,则可以轻松地将其包装在自定义初始化程序中,其中包括对 context.Database.Delete() 的调用。

http://entityframework.codeplex.com/workitem/1689

【讨论】:

  • 感谢您的澄清
【解决方案4】:

给其他卡住的人。我最终在 EF6 中这样做了。

public CatContext(bool forcePreparation)
{
    if (!forcePreparation)
        return;

    if (!Database.Exists() || !Database.CompatibleWithModel(false))
    {
        Database.SetInitializer<CatContext>(new MigrateDatabaseToLatestVersion<CatContext, Configuration>());
    }
    else
    {
        // Database exists and matches the current model.
        // Doing this special thing to avoid the bug that arises sometimes.
        Database.SetInitializer<CatContext>(null);
    }

    Database.Initialize(true);
}

public CatContext()
    : this(forcePreparation: false)
{ }

在我的代码中,我将上下文对象创建为new CatContext(true)

【讨论】:

    猜你喜欢
    • 2021-07-15
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2013-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多