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
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 .
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.