Database.SetInitializer如何实际工作? (EF代码优先创建数据库并使用多个连接字符串应用迁移)

时间:2013-04-05 09:13:49

标签: entity-framework-5 ef-migrations

我正在尝试编写一个方法来创建数据库并在给定连接字符串的情况下对其运行迁移。

我需要多个连接,因为我在单独的数据库中记录审核日志。 我使用像

之类的代码从app.config中获取连接字符串
ConfigurationManager.ConnectionStrings["Master"].ConnectionString;

代码使用我的app.config中定义的第一个连接字符串而不是其他连接字符串,这使我认为它以某种方式从app.config获取连接字符串我不知道。

我创建数据库的代码(如果不存在)是

private static Context MyCreateContext(string ConnectionString)
  {
   // put the connection string where the factory method can get it
   AppDomain.CurrentDomain.SetData("ConnectionString", ConnectionString );
   var factory = new ContextFactory();
   // I know I need this line - but I cant see how what follows actually uses it
   Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context,DataLayer.Migrations.Configuration>());
   var context = factory.Create();
   context.Database.CreateIfNotExists(); 
   return context
   }

Migrations.Configuration中的代码是

Public sealed class Configuration :  DbMigrationsConfiguration<DataLayer.Context>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }
}

上下文工厂代码是

  public class ContextFactory : IDbContextFactory<Context>
{
    public Context Create()
    {
        var s = (string)AppDomain.CurrentDomain.GetData("ConnectionString");

        return new Context(s);
    }
}

因此我在创建上下文之前设置连接字符串 我认为哪里出错了,因为除了数据库名称之外连接字符串都是相同的,并且迁移代码使用一个连接字符串运行,但不与其他字符串一起运行?

我想知道我的问题是否与理解Database.SetInitializer如何实际工作有关。我猜测反射或泛型。如何调用SetInitializer绑定到我的实际上下文?

我尝试过以下代码但迁移不运行

 private static Context MyCreateContext(string ConnectionString)
    {
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, DataLayer.Migrations.Configuration>());
        var context = new Context(ConnectionString);
        context.Database.CreateIfNotExists();
    }

This question appears to be related

更新:

如果我使用连接字符串引用,我可以使迁移工作      public MyContext():base(“MyContextConnection”) - 指向config

如果我创建了一个ContextFactory类并通过引用全局传递连接,我也能够使用不同的上下文实例进行迁移。 (参见我对相关问题链接的回答)

现在我想知道为什么它必须这么难。

2 个答案:

答案 0 :(得分:6)

我不确定你面临的问题是什么,但让我试试

提供连接的最简单方法 - 并确保以这种方式运作......

1)Use your 'DbContext' class name - 并在app.config(或web.config)中定义连接。这是最简单的,你应该有一个与你的上下文类名匹配的连接,

2)如果你通过构造函数将它放入DbContext - 然后保持一致并使用那个。我还建议从配置连接中“读取” - 并再次命名它与上下文类相同(使用连接'name',而不是实际的字符串),

3)如果不存在 - EF / CF根据您的提供商 - 和您的上下文的类名制作“默认” - 这通常不是你想要什么,

  

出于这个原因,您不应该使用初始化程序进行自定义 -   初始化器应该是不可知的并且用于其他目的 - 设置   .config中的连接 - 或直接在DbContext

同时检查此Entity Framework Code First - How do I tell my app to NOW use the production database once development is complete instead of creating a local db?

  

在执行任何操作之前,请务必检查“数据的位置”。

关于初始化程序的实际工作方式 - 查看我的其他帖子,我做了一个详尽的例子

How to create initializer to create and migrate mysql database?

注意:(来自评论)

连接不应该是非常动态的 - 除非你有充分的理由,否则配置是正确的选择 构造函数也应该正常工作。
CreateDbIfNotExists与“迁移”初始化程序无法很好地协同工作。您只需使用MigrateDatabaseToLatestVersion初始值设定项即可。不要'混合'

或者 - 像公共MyContext() : base("MyContextConnection")这样的内容 - 在配置中指向<connectionStrings>

指向连接 - 只需使用其'name'并将其放入构造函数中。

或使用像ConfigurationManager.ConnectionStrings["CommentsContext"].ConnectionString

这样的东西

关于通过迁移来娱乐“多个数据库”(本地和远程来自一个应用) - 不完全相关 - 但这个链接 - Migration not working as I wish... Asp.net EntityFramework

<强>更新 (在此进一步讨论 - Is adding a class that inherits from something a violation of the solid principles if it changes the behavior of code?

这里越来越有趣了。我确实设法重现了你实际面临的问题。以下是我认为正在发生的事情的简短细分:

首先,这很愉快地工作:

Database.SetInitializer(new CreateAndMigrateDatabaseInitializer<MyContext, MyProject.Migrations.Configuration>());
for (var flip = false; true; flip = !flip)
{
    using (var db = new MyContext(flip ? "Name=MyContext" : "Name=OtherContext"))
    {
        // insert some records...
        db.SaveChanges();
    }
}

(我在其他帖子中使用了自定义初始化程序,它可以“手动”控制迁移/创建)

没有初始化程序,工作正常。一旦我打开它,我遇到了一些奇怪的问题。

我删除了Db-s(每个连接两个)。我预计要么不工作,要么创建一个数据库,然后在下一个传递中创建另一个数据库(就像它一样,没有迁移,只是'创建'初始化程序)。

  

令我惊讶的是,它实际上是在第一个上创建了两个数据库   传递??

然后,作为一个好奇的人:),我在MyContext ctor上放置断点,并通过迁移器/初始化程序进行调试。再次为空/没有db-s等。

它在我flip的通话中创建了第一个实例。然后在第一次访问'model'时,它调用了初始化程序。迁移者接管了(没有db-s)。在migrator.Update();期间,它实际构建了MyContext(我在配置中通过通用参数猜测) - 并调用'默认'空ctor。默认情况下,它具有“其他连接/名称” - 并且还创建了另一个Db。

所以,我认为这解释了你正在经历的事情。为什么你必须创建'工厂'来支持Context创建。这似乎是唯一的方法。并设置了一些'AppDomain'宽'连接字符串'(实际上你做得很好),默认ctor调用没有'覆盖'。

我看到的解决方案是 - 你只需要通过工厂运行所有东西 - 并在那里'翻转'连接(不需要静态连接,只要你的工厂是单身。

答案 1 :(得分:0)

您可以在MigrateDatabaseToLatestVersion构造函数中提供配置。 如果在DbContext中设置初始值设定项,您还可以传递“true”以使用当前连接字符串。