在不使用IDbContextFactory的情况下编写第一个自定义连接字符串和迁移代码

Kir*_*eed 8 c# migration ef-code-first

我正在尝试编写一个易于理解的DBContext类,它采用自定义连接字符串,可以运行迁移,并且我允许使用包管理器生成迁移.

我好像在四处走动.

我已经能够使用对我来说非常糟糕的代码来使用它.我在关于连接字符串和迁移的这个问题的答案中记录了这一点.

Radek的答案看起来比我的好,但是我发现当我实现它然后尝试在Package Manager中创建一个迁移时,我得到了消息

目标上下文'DataLayer.Context'不可构造.添加默认构造函数或提供IDbContextFactory的实现.

DataLayer.Context我的上下文类在哪里.

我不想提供一个实现IDbContextFactory(并且Radek的答案似乎表明它不需要)

更新:

如果我包含一个没有参数的构造函数,我可以生成一个迁移.例如

public Context() : base("ConnectionStringName") { }
Run Code Online (Sandbox Code Playgroud)

对于我的上下文创建,我在app.config中传递连接字符串的名称

public Context(string connString) : base(connString)
{
    Database.SetInitializer(new CustomInitializer());
    Database.Initialize(true);
}
Run Code Online (Sandbox Code Playgroud)

最后,我既可以生成迁移,也可以为用户选择的数据库运行迁移.

但是:当我删除数据库然后运行我的应用程序时,我遇到了问题.

我正在使用的初始化代码,来自上面的链接

public class CustomInitializer : IDatabaseInitializer<Context>
{       
    public void InitializeDatabase(Context context)
    {
        try
        {
            if (!context.Database.Exists())
            {
                context.Database.Create();
            }
            else
            {
                if (!context.Database.CompatibleWithModel(false))  
                {
                    var configuration = new Configuration();
                    var migrator = new DbMigrator(configuration);
                    migrator.Configuration.TargetDatabase =
                        new DbConnectionInfo(context.Database.Connection.ConnectionString);
                    IEnumerable<string> migrations = migrator.GetPendingMigrations();
                    foreach (string migration in migrations)
                    {
                        var scriptor = new MigratorScriptingDecorator(migrator);
                        string script = scriptor.ScriptUpdate(null, migration);
                        context.Database.ExecuteSqlCommand(script);
                    }
                }
            }
        }
        catch (Exception ex)
        {
        }
    }       
}
Run Code Online (Sandbox Code Playgroud)

当我删除数据库时,会创建一个新数据库但它没有表.那是因为我的表创建代码都在我的第一次迁移中.

所以!context.Database.CompatibleWithModel(false)条件中的代码不会运行.

然而,唉,代码也不会第二次运行它应该有metadatamodel.

现在尝试让迁移运行......

SADNESS:到目前为止,没有Radek的自定义初始化程序.

根据NSGaga的评论,如果我改变连接,我已经退出了应用程序.

var master = new myMDIForm();
master.ConnectionType = connectionType;   // being an enum of the different connection names in app.config
while (master.ConnectionType != ConnectionType.None )
{
   Application.Run(master);
}
Run Code Online (Sandbox Code Playgroud)

NSG*_*aga 7

(注意:我不知道你的自定义初始化程序,我只是看到了 - 这是connection caching问题和迁移初始化程序的一般解决方案- 但应该使用变体)


这对我有用 - 并且基于我在这里描述的错误行为.

static string _connection;
public MyContext()
    : base(_connection ?? "DefaultConection")
{
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyNamespace.Migrations.Configuration>());
}
public MyContext(string connection)
    : base(connection)
{
    _connection = connection;
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyNamespace.Migrations.Configuration>());
}
Run Code Online (Sandbox Code Playgroud)

几点:

1)static在你的上下文中定义连接 - 当你设置'新的'时,改变它以保持连接latest value.这有助于保持事物同步 - 无论谁访问DbContext都有相同的(它在概念上类似于Factory眼睛更容易,

2)问题Migrations在于 - MigrateDatabaseToLatestVersion在内部缓存连接(以及内部的整个配置)readonly- 即使你在自己DbContext或外面设置的任何内容中更改它,它也会"不同步".

没有办法解决这个问题,而是制作一个"新的初始化程序".

3)@ Radek发现的实际上是该问题的解决方案 - 并符合(2).我只是删除了Initialize(true)它,因为它是不必要的 - 当'时间合适'时会被调用.


就是这样.

有了这个,我现在可以flip全天候连接 - 就像我想要的那样.
(这意味着我可以更改连接runtime- 迁移/创建两个或更多数据库并继续更改连接并同时处理它们)

这是我实际用来在连接之间循环的代码......

for (var flip = true; true; flip = !flip)
{
    using (var db = new MyContext(flip ? "Name=DefaultConnection" : "Name=OtherConnection"))
    {
        // usual db code
    }
}
Run Code Online (Sandbox Code Playgroud)

关键是要set initializer each time you set the connection.
旧的仍然存在 - 和旧连接(在应用程序运行时无法删除Db)