Database.SetInitializer实际上如何工作? (EF代码优先创建数据库,并使用多个连接字符串应用迁移) [英] How does Database.SetInitializer actually work? (EF code-first create database and apply migrations using several connection strings)

查看:129
本文介绍了Database.SetInitializer实际上如何工作? (EF代码优先创建数据库,并使用多个连接字符串应用迁移)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在给定连接字符串的情况下,我试图编写一种方法来创建数据库并在数据库上运行迁移。

I am trying to write a method to create a database and run migrations on it, given the connection string.

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

I need the multiple connections because I record an audit log in a separate database. I get the connection strings out of app.config using code like

ConfigurationManager.ConnectionStrings["Master"].ConnectionString;

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

The code works with the first connection string defined in my app.config but not others, which leads me to think that somehow it is getting the connection string from app.config in some manner I don't know.

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

My code to create the database if it does not exist is

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中的代码为

The code in the Migrations.Configuration is

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);
    }
}

因此,我在创建上下文之前设置了连接字符串

鉴于连接字符串除数据库名称外都是相同的,并且迁移代码仅使用一个连接字符串运行,而不能使用其他连接字符串运行,我该怎么做呢?

Thus I am setting the connection string before creating the context.
Where can I be going wrong, given that the connection strings are all the same except the database name, and the migration code runs with one connection string, but doesnt run with others?

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

I wonder if my problem is to do with understanding how How does Database.SetInitializer actually works. I am guessing something about reflection or generics. How do i make the call to SetInitializer tie tie to my actual context?

我尝试了以下代码,但迁移未运行

I have tried the following code but the migrations do not run

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

此问题似乎与

更新:

如果我使用
public MyContext():base( MyContextConnection)-指向配置中的连接字符串来引用连接字符串,则可以正常进行迁移

I can get the migrations working if I refer to the connection string using public MyContext() : base("MyContextConnection") - which points to in the config

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

I was also able to get migrations working on using different instances of the context, if I created a ContextFactory class and passed the connection to it by referencing a global. ( See my answer to the related question link )

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

Now I am wondering why it has to be so hard.

推荐答案

我不确定您面临的问题是什么,但是让我尝试一下

I'm not sure exactly as to what the problems are you facing, but let me try

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

The easiest way to provide connection - and be sure it works that way...

1)使用您的'DbContext'类名-并在app.config(或web.config)中定义一个连接。这是最简单的,您应该在那里建立一个与您的上下文类名称匹配的连接,

1) Use your 'DbContext' class name - and define a connection in the app.config (or web.config). That's easiest, you should have a connection there that matches your context class name,

2)如果通过构造函数将其放入DbContext,则保持一致并使用该名称。我还建议从配置连接中读取-并再次将其命名为与上下文类相同(使用连接名称,而不是实际的字符串),

2) If you put it into the DbContext via constructor - then be consistent and use that one. I'd also suggest to 'read' from config connections - and again name it 'the same' as your context class (use the connection 'name', not the actual string),

3)如果不存在-EF / CF将默认设置为 -根据您的提供商-以及您上下文的类名-通常不是

3) if none is present - EF/CF makes the 'default' one - based on your provider - and your context's class name - which usually isn't what you want,


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

也请检查此首先使用实体​​框架代码-开发完成后,如何告诉我的应用现在使用生产数据库而不是创建本地数据库?


在执行任何操作之前,始终检查数据的存放位置。

Always check 'where your data' goes - before doing anything.

初始化程序的实际工作方式 -查看我的另一篇文章,我做了一个详尽的例子

For how the initializer actually works - check this other post of mine, I made a thorough example

如何创建用于创建和迁移mysql数据库的初始化程序?

注意:(从评论中)

连接不是动态的-配置是正确的连接,除非您有

构造函数也应该可以正常工作。

CreateDbIfNotExists 与'migration'初始化程序不能很好地配合。您可以只使用 MigrateDatabaseToLatestVersion 初始化程序。不要混合它

Connection shouldn't be very dynamic - config is the right place for it to be, unless you have a good reason.
Constructor should work fine too.
CreateDbIfNotExists doesn't work well together with the 'migration' initializer. You can just use the MigrateDatabaseToLatestVersion initializer. Don't 'mix' it

或者-将类似public MyContext()的内容:base( MyContextConnection)-指向配置中的< connectionStrings>

Or - put something like public MyContext() : base("MyContextConnection") - which points to <connectionStrings> in the config

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

To point to connection - just use its 'name' and put that into constructor.

或使用类似 ConfigurationManager.ConnectionStrings [ CommentsContext]。ConnectionString

关于通过迁移(从一个应用程序本地和远程)进行娱乐的多个数据库-不完全相关-但这链接-迁移未如我所愿... Asp.net EntityFramework

Regarding entertaining 'multiple databases' with migrations (local and remote from one app) - not exactly related - but this link - Migration not working as I wish... Asp.net EntityFramework

更新:
(此处进一步讨论-是否正在添加一个类,如果它改变了代码的行为,则该类将从某种东西继承而来,违反了坚实的原则?

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

It is getting interesting here. I did manage to reproduce the problems you're facing actually. Here is a short breakdown on what I think it's happening:

首先,这很愉快:

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();
    }
}

(我从其他帖子中使用了自定义初始化程序,

(I used custom initializer from my other post, which controls migration/creation 'manually')

不需要初始化程序就可以很好地工作。一旦打开它,我就会遇到一些奇怪的问题。

That worked fine w/o an Initializer. Once I switched that on, I ran into some curious problems.

我删除了Db-s(每个连接两个)。我希望要么不起作用,要么创建一个数据库,然后在下一遍创建另一个数据库(就像那样,不进行迁移,只是创建初始化程序)。

I deleted Db-s (two, for each connection). I expected to either not work, or create one db, then another in the next pass (like it did, w/o migrations, just 'Create' initializer).


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

What happened, to my surprise - is it actually created both databases on the first pass ??

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

Then, being a curious person:), I put breakpoints on the MyContext ctor, and debugged through the migrator/initializer. Again empty/no db-s etc.

它在 flip 内的呼叫中创建了第一个实例。然后,在首次访问模型时,它调用了初始化程序。迁移者接管了(没有db-s)。在 migrator.Update(); 期间,它实际上构造了 MyContext (我猜是通过Configuration中的通用参数) -并调用默认空ctor。默认情况下具有其他连接/名称-并同时创建另一个Db。

It created first instance on my call within the flip. Then on the first access to 'model', it invoked the initializer. Migrator took over (having had no db-s). During the migrator.Update(); it actually constructs the MyContext (I'm guessing via generic param in Configuration) - and calls the 'default' empty ctor. That had the 'other connection/name' by default - and creates the other Db all as well.

所以,我认为这可以解释您遇到的情况。以及为什么必须创建工厂来支持上下文创建。那似乎是唯一的方法。并设置一些 AppDomain宽的连接字符串(您实际上做得很好),默认情况下,该字符串不会覆盖 ctor调用。

So, I think this explains what you're experiencing. And why you had to create the 'Factory' to support the Context creation. That seems to be the only way. And setting some 'AppDomain' wide 'connection string' (which you did well actually) which isn't 'overriden' by default ctor call.

我看到的解决方案是-您只需要通过工厂运行所有内容-并在其中进行翻转连接(不需要静态连接,只要您的工厂是

Solution that I see is - you just need to run everything through factory - and 'flip' connections in there (no need for static connection, as long as your factory is a singleton.

这篇关于Database.SetInitializer实际上如何工作? (EF代码优先创建数据库,并使用多个连接字符串应用迁移)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆