使用 Entity Framework 核心迁移命令时无法附加数据库文件

Cannot attach database file when using Entity Framework Core Migration commands

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

请帮助如何将 db 文件附加到另一个位置。 谢谢

可能有一个不同的 *.mdf 文件已附加到名为 EfGetStarted2 的数据库...请尝试 dropping/detaching 该数据库,然后重试。

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

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

它远谈不上好,但我还是用它来进行集成测试。对于 "real world" 使用 EF 迁移的场景应该是可行的方法,但也许这个问题的根本原因是相同的...

更新

下一版本将包括 support for AttachDBFilename