Ошибка использования базы данных с первым кодом Entity Framework 4


у меня есть приложение MVC3 и EF 4 Code First, которое настроено на изменение DB при изменении модели, установив инициализатор DB в DropCreateDatabaseIfModelChanges<TocratesDb>, где TocratesDb мой производным DbContext.

теперь я внес изменения в модель, добавив свойства в класс, но когда EF пытается удалить и воссоздать БД, я получаю следующую ошибку:

Cannot drop database "Tocrates" because it is currently in use.

у меня нет абсолютно никаких других соединений, открытых в этой базе данных. Я предполагаю, что мой cDbContext все еще есть открытое соединение с базой данных, но что я могу с этим поделать?

NEW: теперь моя проблема заключается в том, как повторно создать базу данных на основе модели. Используя более общий IDatabaseInitializer, я теряю это и должен реализовать его сам.

6   51   2011-03-13 14:12:39

6 ответов:

ваш текущий контекст должен иметь открытое соединение, чтобы иметь возможность удалить базу данных. Проблема в том, что могут быть другие открытые соединения, которые будут блокировать ваш инициализатор БД. Одним из очень хороших примеров является открытие любой таблицы из вашей базы данных в Management studio. Другой возможной проблемой могут быть открытые соединения в пуле соединений вашего приложения.

в MS SQL этого можно избежать, например, переключив БД в однопользовательский режим и принудительно подключив все соединения для закрытия и отката незавершенных транзакций:

ALTER DATABASE Tocrates SET SINGLE_USER WITH ROLLBACK IMMEDIATE

вы можете создать новый intializer, который сначала вызовет эту команду, а затем отбросит базу данных. Имейте в виду, что вы должны обрабатывать подключение к базе данных самостоятельно, потому что ALTER DATABASE и DROP DATABASE должен быть вызван по тому же соединению.

Edit:

здесь у вас есть пример использования шаблона декоратора. Вы можете изменить его и инициализировать внутренний инициализатор внутри конструктора вместо его передачи в качестве параметра.

public class ForceDeleteInitializer : IDatabaseInitializer<Context>
{
    private readonly IDatabaseInitializer<Context> _initializer;

    public ForceDeleteInitializer(IDatabaseInitializer<Context> innerInitializer)
    {
        _initializer = innerInitializer;    
    }

    public void InitializeDatabase(Context context)
    {
        context.Database.SqlCommand("ALTER DATABASE Tocrates SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
        _initializer.InitializeDatabase(context);
    }
}

Я нашел в EF 6 это не удается с ALTER DATABASE statement not allowed within multi-statement transaction ошибка.

решение состояло в том, чтобы использовать новую перегрузку поведения транзакции следующим образом:

context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, "ALTER DATABASE [" + context.Database.Connection.Database + "] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");

У меня была та же проблема.

Я разрешил его, закрыв соединение, открытое в представлении обозревателя серверов Visual Studio.

Я понимаю, что это датировано, но я не мог заставить принятое решение работать, поэтому я свернул быстрое решение...

using System;
using System.Data.Entity;

namespace YourCompany.EntityFramework
{
    public class DropDatabaseInitializer<T> : IDatabaseInitializer<T> where T : DbContext, new()
    {
        public DropDatabaseInitializer(Action<T> seed = null)
        {
            Seed = seed ?? delegate {};
        }

        public Action<T> Seed { get; set; }

        public void InitializeDatabase(T context)
        {
            if (context.Database.Exists())
            {
                context.Database.ExecuteSqlCommand("ALTER DATABASE [" + context.Database.Connection.Database + "] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
                context.Database.ExecuteSqlCommand("USE master DROP DATABASE [" + context.Database.Connection.Database + "]");
            }

            context.Database.Create();

            Seed(context);
        }
    }
}

это работает для меня и поддерживает легко высева.

в Visual Studio 2012,Обозреватель объектов SQL Server окно может содержать подключение к базе данных. Закрытие окна и все открытые из него окна освобождает соединение.

простое закрытие всего моего проекта и его повторное открытие сделали трюк для меня. Это самый простой способ убедиться, что все еще нет открытых соединений