使用Entity Framework Core Migration命令时无法附加数据库文件

时间:2015-08-10 19:54:28

标签: entity-framework-core

我正在使用EntityFramework Core命令迁移数据库。我正在使用的命令就像文档建议的那样:dnx。如果迁移适用。问题是在连接字符串中指定AttachDbFileName时,会出现以下错误:无法将数据库文件附加为数据库xxxxxxx。这是我正在使用的连接字符串: 数据源=(LocalDB)\ mssqllocaldb;集成安全性= True;初始目录= EfGetStarted2; AttachDbFileName = D:\ EfGetStarted2.mdf

请帮助您如何将db文件附加到其他位置。 感谢

2 个答案:

答案 0 :(得分:0)

可能有一个不同的* .mdf文件已经附加到名为EfGetStarted2的数据库...尝试删除/分离该数据库,然后再试一次。

如果用户LocalDB正在运行,您可能也会遇到问题,因为它没有对路径的正确权限。

答案 1 :(得分:0)

EF核心似乎在使用 AttachDbFileName 时遇到了麻烦,或者根本没有处理它。

  • EnsureDeleted将数据库名称更改为 master 但保留任何 AttachDbFileName 值,这会导致错误,因为我们无法附加 master 数据库到另一个文件。
  • EnsureCreated使用提供的 AttachDbFileName 值打开连接,这会导致错误,因为我们要创建的数据库文件尚不存在。

EF6有一些处理这些用例的逻辑,请参阅SqlProviderServices.DbCreateDatabase,所以一切都很顺利。

作为一种解决方法,我编写了一些hacky代码来处理这些场景:

public static void EnsureDatabase(this DbContext context, bool reset = false)
{
    if (context == null)
        throw new ArgumentNullException(nameof(context));

    if (reset)
    {
        try
        {
            context.Database.EnsureDeleted();
        }
        catch (SqlException ex) when (ex.Number == 1801)
        {
            // HACK: EF doesn't interpret error 1801 as already existing database
            ExecuteStatement(context, BuildDropStatement);
        }
        catch (SqlException ex) when (ex.Number == 1832)
        {
            // nothing to do here (see below)
        }
    }

    try
    {
        context.Database.EnsureCreated();
    }
    catch (SqlException ex) when (ex.Number == 1832)
    {
        // HACK: EF doesn't interpret error 1832 as non existing database
        ExecuteStatement(context, BuildCreateStatement);

        // this takes some time (?)
        WaitDatabaseCreated(context);

        // re-ensure create for tables and stuff
        context.Database.EnsureCreated();
    }
}

private static void WaitDatabaseCreated(DbContext context)
{
    var timeout = DateTime.UtcNow + TimeSpan.FromMinutes(1);

    while (true)
    {
        try
        {
            context.Database.OpenConnection();
            context.Database.CloseConnection();
        }
        catch (SqlException)
        {
            if (DateTime.UtcNow > timeout)
                throw;
            continue;
        }
        break;
    }
}

private static void ExecuteStatement(DbContext context, Func<SqlConnectionStringBuilder, string> statement)
{
    var builder = new SqlConnectionStringBuilder(context.Database.GetDbConnection().ConnectionString);

    using (var connection = new SqlConnection($"Data Source={builder.DataSource}"))
    {
        connection.Open();

        using (var command = connection.CreateCommand())
        {
            command.CommandText = statement(builder);
            command.ExecuteNonQuery();
        }
    }
}

private static string BuildDropStatement(SqlConnectionStringBuilder builder)
{
    var database = builder.InitialCatalog;

    return $"drop database [{database}]";
}

private static string BuildCreateStatement(SqlConnectionStringBuilder builder)
{
    var database = builder.InitialCatalog;

    var datafile = builder.AttachDBFilename;
    var dataname = Path.GetFileNameWithoutExtension(datafile);

    var logfile = Path.ChangeExtension(datafile, ".ldf");
    var logname = dataname + "_log";

    return $"create database [{database}] on primary (name = '{dataname}', filename = '{datafile}') log on (name = '{logname}', filename = '{logfile}')";
}

这远不是很好,但无论如何我都在使用它进行集成测试。对于使用EF迁移的“真实世界”场景应该是可行的方法,但也许这个问题的根本原因是相同的......

<强>更新

下一个版本将包含support for AttachDBFilename