Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

ALTER DATABASE Tocrates SET SINGLE_USER WITH ROLLBACK IMMEDIATE

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

Edit:

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

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

Ответ 2

В 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");

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

В Visual Studio 2012 окно SQL Server Object Explorer может содержать соединение с базой данных. Закрытие окна, и все открытые из него окна освобождают соединение.

Ответ 6

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