No se puede adjuntar un archivo de base de datos cuando se usan los comandos de migración de Entity Framework Core

entity-framework-core

Pregunta

Estoy usando los comandos de EntityFramework Core para la base de datos de migración. El comando que estoy usando es como sugiere la documentación: dnx. Se aplica la migración ef. El problema es cuando se especifica AttachDbFileName en la cadena de conexión, aparece el siguiente error: No se puede adjuntar el archivo de base de datos como base de datos xxxxxxx. Esta es la cadena de conexión que estoy usando: Fuente de datos = (LocalDB) \ mssqllocaldb; Seguridad integrada = Verdadera; Catálogo inicial = EfGetStarted2; AttachDbFileName = D: \ EfGetStarted2.mdf

Por favor, ayuda cómo adjuntar el archivo db a otra ubicación. Gracias

Respuesta aceptada

El núcleo de EF parece tener problemas con AttachDbFileName o no lo maneja en absoluto.

  • EnsureDeleted que el nombre de la base de datos cambia a maestro pero mantiene cualquier valor de AttachDbFileName , lo que genera un error, ya que no podemos adjuntar la base de datos maestra a otro archivo.
  • EnsureCreated abre una conexión utilizando el valor AttachDbFileName proporcionado, lo que genera un error, ya que el archivo de la base de datos que queremos crear aún no existe.

EF6 tiene cierta lógica para manejar estos casos de uso, consulte SqlProviderServices.DbCreateDatabase , por lo que todo funcionó bastante bien.

Como solución, escribí un código hacky para manejar estos escenarios:

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

Está lejos de ser agradable, pero de todos modos lo estoy usando para las pruebas de integración. Para los escenarios del "mundo real", usar migraciones de EF debería ser el camino a seguir, pero quizás la causa raíz de este problema sea la misma ...

Actualizar

La próxima versión incluirá soporte para AttachDBFilename .


Respuesta popular

Es posible que ya haya un archivo * .mdf diferente adjunto a una base de datos llamada EfGetStarted2 ... Intente soltar / separar esa base de datos e intente nuevamente.

También es posible que tenga problemas si el usuario LocalDB se está ejecutando ya que no tiene los permisos correctos para la ruta.




Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué