Impossible de joindre un fichier de base de données lors de l'utilisation des commandes de migration Entity Framework Core

entity-framework-core

Question

J'utilise les commandes EntityFramework Core pour la base de données de migration. La commande que j'utilise est comme le suggère la documentation: dnx. ef migration s’applique. Le problème survient lors de la spécification de AttachDbFileName dans la chaîne de connexion. L'erreur suivante s'affiche: Impossible de joindre le fichier de base de données en tant que base de données xxxxxxx. Il s'agit de la chaîne de connexion que j'utilise: source de données = (LocalDB) \ mssqllocaldb; sécurité intégrée = True; catalogue initial = EfGetStarted2; AttachDbFileName = D: \ EfGetStarted2.mdf

S'il vous plaît aider à joindre le fichier db à un autre emplacement. Merci

Réponse acceptée

Le noyau EF semble avoir des problèmes avec AttachDbFileName ou ne le gère pas du tout.

  • EnsureDeleted change le nom de la base de données en maître mais conserve toute valeur AttachDbFileName , ce qui entraîne une erreur car nous ne pouvons pas attacher la base de données master à un autre fichier.
  • EnsureCreated ouvre une connexion à l'aide de la valeur AttachDbFileName fournie, ce qui entraîne une erreur, car le fichier de la base de données à créer n'existe pas encore.

EF6 a une certaine logique pour gérer ces cas d'utilisation, voir SqlProviderServices.DbCreateDatabase , donc tout a bien fonctionné.

En guise de solution de contournement, j'ai écrit du code hacky pour gérer ces scénarios:

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

C'est loin d'être agréable, mais je l'utilise quand même pour les tests d'intégration. Les scénarios "réels" utilisant des migrations EF devraient être la solution, mais peut-être que la cause première de ce problème est la même ...

Mettre à jour

La prochaine version inclura le support pour AttachDBFilename .


Réponse populaire

Un fichier * .mdf différent peut déjà être attaché à une base de données nommée EfGetStarted2 ... Essayez de supprimer / détacher cette base de données, puis réessayez.

Vous pouvez également rencontrer des problèmes si l'utilisateur LocalDB est en cours d'exécution car il ne dispose pas des autorisations appropriées sur le chemin.



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi