Ошибка использования базы данных с 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, я теряю это и сам должен его реализовать.
Ответы
Ответ 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
Простое закрытие всего моего проекта и его повторное открытие сделали трюк для меня. Это самый простой способ убедиться, что соединения еще не открыты.