DropCreateDatabaseIfModelChanges EF6 приводит к ошибке System.InvalidOperationException: модель, поддерживающая контекст, изменилась - программирование
Подтвердить что ты не робот

DropCreateDatabaseIfModelChanges EF6 приводит к ошибке System.InvalidOperationException: модель, поддерживающая контекст, изменилась

После перехода на Entity Framework 6 я получаю сообщение об ошибке при выполнении модульных тестов на сервере сборки.

Я использую инициализатор DropCreateDatabaseIfModelChanges. Когда я меняю его на MigrateDatabaseToLatestVersion, все работает, но я хочу придерживаться прежнего инициализатора.

Ошибка, которую я получаю:

System.InvalidOperationException: System.InvalidOperationException: Модель, поддерживающая контекст "AppContext", изменилась с тех пор, как была создана база данных. Рассмотрите возможность использования Первых Миграций Кода для обновления база данных (http://go.microsoft.com/fwlink/?LinkId=238269)..

Что правильно, оно изменилось, но с инициализатором DropCreateDatabaseIfModelChanges он должен быть воссоздан. Любые идеи?

EF настроен в App.config. Здесь соответствующая часть:

<connectionStrings>
    <add name="AppContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=my.app.unittest;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
<entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
        <parameters>
            <parameter value="v11.0" />
        </parameters>
    </defaultConnectionFactory>
    <contexts>
        <context type="my.app.core.Data.AppContext, my.app.core">
            <databaseInitializer type="System.Data.Entity.DropCreateDatabaseIfModelChanges`1[[my.app.core.Data.AppContext, my.app.core]], EntityFramework" />
        </context>
    </contexts>
    <providers>
        <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
</entityFramework>
4b9b3361

Ответ 1

Ну, похоже, что EF 6.0 вводит новое правило:

"Если DbContext использует инициализатор, а Migrations настроены, генерируйте исключение при создании модели".

До и включая EF 6 RC это не было выполнено. Досадной частью является то, что "Миграции настроены" определяется реализацией DbMigrationsConfiguration. По-видимому, не существует способа программно отключить миграцию в тестах - если вы внедрили

Я работал вокруг этого очень похоже на Sebastian Piu - мне пришлось избавиться от класса Configuration из моих тестов, но я не мог просто удалить его, потому что мы используем Migrations для нашего основного проекта. Argh!

Это был мой код раньше:

public class MyDbContext : DbDContext, IMyDbContext
{
  public IDbSet<Users> Users {get; set;}
  public IDbSet<Widgets> Widgets {get; set;}
}

// Migrations are considered configured for MyDbContext because this class implementation exists.
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
  public Configuration()
  {
    AutomaticMigrationsEnabled = false;
  }
}

// Declaring (and elsewhere registering) this DB initializer of type MyDbContext - but a DbMigrationsConfiguration already exists for that type.
public class TestDatabaseInitializer : DropCreateDatabaseAlways<MyDbContext>
{
    protected override void Seed(MyDbContext context) { }
}

Я столкнулся с исключением System.InvalidOperationException, когда DbContext инициализировался в моем тестовом коде. Поскольку приложение не использует инициализатор, никаких проблем с запуском приложения не было. Это только нарушило мои тесты.

Решение (которое больше похоже на обходное решение для вещей, отсутствующих в EF), заключается в сегментировании инициализатора и конфигурации DbMigrationsConfiguration, поэтому только один из них отображается в среде выполнения. Я хочу, чтобы мои тесты использовали Initializer, и я хочу, чтобы мое приложение использовало DbMigrationsConfiguration. Это можно сделать более чисто, если у DbContext есть интерфейс, но, увы, он реализует только IObjectContextAdapter.

Сначала я сделал реферат DbContext:

public abstract class MyDbContextBase : DbContext, IMyDbContext
{
      public IDbSet<Users> Users {get; set;}
      public IDbSet<Widgets> Widgets {get; set;}
}

Затем я получил 2 класса:

public class MyDbContext : MyDbContextBase
{
  public MyDbContext(string connectionStringOrName, IDatabaseInitializer<MyDbContext> dbInitializer) 
    : base(connectionStringOrName)
  {
  }
}

public class MyTestDbContext : MyDbContextBase
{
  public MyTestDbContext(string connectionStringOrName, IDatabaseInitializer<MyDbContext> dbInitializer) 
    : base(connectionStringOrName)
  {
    Database.SetInitializer(dbInitializer);
  }
}

Оба MyDbContext и MyTestDbContext - это IMyDbContexts, поэтому ваша существующая установка впрыска зависимостей должна работать без изменения. Я тестировал только Spring.NET.

My DbMigrationsConfiguration реализует производный тип, который НЕ используется в тестах:

internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
  public Configuration()
  {
    AutomaticMigrationsEnabled = false;
  }
}

Наконец, тип инициализатора был перенесен в производный тип тестового класса:

public class TestDatabaseInitializer : DropCreateDatabaseAlways<MyTestDbContext>
{
    protected override void Seed(MyTestDbContext context) { }
}

Я могу подтвердить, что мои тесты проходят, и мое приложение (и Migrations) все еще работает по-прежнему.

Ответ 2

Я нашел ту же проблему сразу после обновления до EF6. После прочтения комментария Стефана и тех же симптомов, которые он описывает (тесты загружали класс Configuration из моего основного проекта)

Решение/обходной путь в моем случае состоял в

  • создайте новый class TestContext: MyDataContext в моем проекте "Тесты"
  • измените инициализатор от DropCreateDatabaseAlways<MyDataContext> до DropCreateDatabaseAlways<TestContext>
  • обновить/обобщить места, где я создал свой реальный контекст, чтобы использовать тестовый

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

Ответ 3

Это бросается, потому что вы включили миграцию, и вы используете инициализатор DropCreateDatabaseIfModelChanges. Entityframework не поддерживает использование этого инициализатора с миграциями. У вас есть два варианта:

  • Отключить инициализатор

или

  • Отключить миграцию, удалив конфигурацию миграции

Ответ 4

Похоже, что это было предназначено. Вот цитата одного из разработчиков:

Это изменение поведения было задумано, потому что EF5 создаст базу данных, не используя определенные миграции, что означает, что база данных, созданная инициализатором, может отличаться от базы, созданной Migrations. Это может привести к тестированию в отношении одной схемы базы данных, но в процессе ее работы с другой схемой базы данных. Тем не менее, мы предварительно решили внести изменения в это поведение, и это отслеживается здесь: https://entityframework.codeplex.com/workitem/1709

Ответ 5

Что отлично работает для меня, это исключение миграции с помощью define. Вот как:

  • Создайте новую конфигурацию с именем Test, которая определяет Test
  • В ваших тестах введите ошибку, если Test не определен.
  • Исключить ваши миграции при определении Test:
#if !TEST
internal sealed class Configuration : DbMigrationsConfiguration<Context>
{
    //...
}
#endif

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