EF 4.3 Автоматическая миграция с несколькими DbContexts в одной базе данных
Я пытаюсь использовать EF 4.3 миграции с несколькими кодами в начале DbContexts. Мое приложение разделено на несколько плагинов, которые, возможно, имеют собственный DbContext в отношении своего домена. Приложение должно использовать одну базу данных sql.
Когда я пытаюсь автоматически перенести контексты в пустой базе данных, это успешно только для первого контекста. Для любого другого контекста необходимо, чтобы для AutomaticMigrationDataLossAllowed-Property было установлено значение true, но затем он пытается отбросить таблицы предыдущей.
Итак, мой вопрос:
- Как я могу указать конфигурации миграции только для того, чтобы следить за таблицами, определенными в их соответствующем контексте, и оставить все остальные в покое?
- Каков правильный рабочий процесс для работы с несколькими DbContexts с автоматической миграцией в одной базе данных?
Спасибо!
9 ответов
Code First Migrations предполагает, что существует только одна конфигурация миграций на базу данных (и один контекст на конфигурацию).
Я могу придумать два возможных решения:
Создайте совокупный контекст, который включает в себя все сущности каждого контекста и ссылается на этот "супер" контекст из вашего класса конфигурации миграций. Таким образом, все таблицы будут созданы в базе данных пользователя, но данные будут только в тех, для которых они установили плагины.
Используйте отдельные базы данных для каждого контекста. Если у вас есть общие сущности между контекстами, добавьте пользовательскую миграцию и замените
CreateTable(...)
позвонить сSql("CREATE VIEW ...")
вызов для получения данных из "исходной" базы данных объекта.
Я бы попробовал #1, поскольку он хранит все в одной базе данных. Вы можете создать отдельный проект в своем решении, который будет содержать ваши миграции и этот "супер" контекст. Просто добавьте проект, сделайте ссылку на все проекты ваших плагинов, создайте контекст, включающий все сущности, затем вызовите Enable-Migrations для этого нового проекта. После этого все должно работать как положено.
Вот что вы можете сделать. очень просто.
Вы можете создать класс миграции для каждого вашего контекста. например
internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
public Configuration1 (){
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "YourProject.Models.ContextNamespace1";
}
}
internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
public Configuration2 (){
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "YourProject.Models.ContextNamespace2";
}
}
Теперь вы добавляете миграцию. Вам не нужно включать миграцию, так как вы уже сделали с 2 классами выше.
Add-Migration -configuration Configuration1 Context1Init
Это создаст скрипт миграции для context1. Вы можете повторить это снова для других контекстов.
Add-Migration -configuration Configuration2 Context2Init
Обновить вашу базу данных
Update-Database -configuration Configuration1
Update-Database -configuration Configuration2
Это можно сделать в любом порядке. За исключением того, что вам нужно убедиться, что каждая конфигурация вызывается последовательно.
У меня есть рабочий сайт с несколькими контекстами, использующими миграции. Однако вам нужно использовать отдельную базу данных для контекста, и все это происходит из класса *Configuration в пространстве имен Migrations вашего проекта, поэтому, например, CompanyDbContext указывает на Company.sdf с помощью CompanyConfiguration. update-database -configurationtypename CompanyConfiguration
, Другой LogDbContext указывает на Log.sdf, используя LogConfiguration и т. Д.
Учитывая это, пытались ли вы создать 2 контекста, указывающих на одну и ту же базу данных и говорящих создателю модели игнорировать список таблиц другого контекста?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<OtherContextsClass>();
// more of these
}
Так как миграции работают с ModelBuilder, это может сделать работу.
Дерзкая альтернатива - избегать использования автоматических миграций, генерировать миграцию каждый раз, а затем вручную просеивать и удалять ненужные операторы, а затем запускать их, хотя ничто не мешает вам создать простой инструмент, который просматривает контексты и сгенерированные операторы и выполняет миграционные исправления для вас.
Хорошо, я боролся с этим в течение дня, и вот решение для тех, кто ищет ответ...
Я предполагаю, что большинство людей, читающих этот пост, присутствуют здесь, потому что у них есть большой класс DbContext с большим количеством свойств DbSet<>, и загрузка занимает много времени. Вы, наверное, подумали, да, это имеет смысл, я должен разделить контекст, так как я не буду использовать все наборы dbsets одновременно, и я буду загружать только "Частичный" контекст, основываясь на ситуации, где мне нужно Это. Таким образом, вы разделили их, только чтобы узнать, что миграция Code First не поддерживает ваш способ революционного мышления.
Таким образом, ваш первый шаг должен был разделить контексты, затем вы добавили класс MigrationConfiguration для каждого из новых контекстов, вы добавили строки подключения, названные точно так же, как ваши новые классы Context.
Затем вы попытались запустить новые разделенные контексты один за другим, выполнив Add-Migration Context1, а затем выполнив Update-Database -Verbose...
Казалось, все работает нормально, но затем вы замечаете, что каждая последующая миграция удаляла все таблицы из предыдущей миграции и оставляла таблицы только после самой последней миграции.
Это связано с тем, что текущая модель миграции предполагает наличие одного DbContext на базу данных, и это должно быть зеркальным соответствием.
То, что я также попробовал, и кто-то предложил здесь сделать это, - создать один SuperContext, в котором есть все наборы БД. Создайте отдельный класс Migration Configuration и запустите его. Оставьте свои частичные классы Context на месте и попытайтесь их создать и использовать. EF жалуется, что модель поддержки изменилась. Опять же, это связано с тем, что EF сравнивает ваш частичный dbcontext с сигнатурой контекста All-Sets, которая была оставлена после миграции Super Context.
На мой взгляд, это серьезный недостаток.
В моем случае я решил, что ЭФФЕКТИВНОСТЬ важнее миграции. Итак, что я в итоге сделал, так это то, что после того, как я запустился в контексте Super и собрал все таблицы, я вошел в базу данных и вручную удалил таблицу _MigrationHistory.
Теперь я могу создавать и использовать мои частичные контексты без EF на это жаловаться. Он не находит таблицу MigrationHistory и просто движется дальше, что позволяет мне иметь "Частичное" представление базы данных.
Компромисс, конечно, заключается в том, что любые изменения в модели нужно будет вручную распространять в базе данных, поэтому будьте осторожны.
Это сработало для меня, хотя.
Я только столкнулся с этой проблемой и понял, что причина, по которой я разделил их на разные контексты, заключалась просто в том, чтобы сгруппировать связанные модели в управляемые куски, а не по какой-либо другой технической причине. Вместо этого я объявил свой контекст как частичный класс, и теперь разные файлы кода с разными моделями могут добавлять DbSets в DbContext.
Таким образом, магия автомиграции все еще работает.
Как упомянуто выше Брайсом, наиболее практичное решение - иметь 1 супер DbContext на приложение / базу данных.
Необходимость использовать только 1 DbContext для всего приложения, по-видимому, является серьезным техническим и методологическим недостатком, поскольку он влияет на модульность, помимо прочего. Кроме того, если вы используете службы данных WCF, вы можете использовать только 1 DataService на приложение, поскольку DataService может отображать только 1 DbContext. Так что это значительно меняет архитектуру.
С другой стороны, небольшое преимущество состоит в том, что весь код миграции, связанный с базой данных, централизован.
У меня это работает с ручными миграциями, но вы не можете понизить версию, так как она не может различить конфигурации в таблице __MigrationHistory. Если я попытаюсь понизить версию, тогда он будет считать миграции из других конфигураций автоматическими, и, поскольку я не допускаю потери данных, произойдет сбой. Мы будем использовать его только для обновления, хотя оно работает для наших целей.
Тем не менее, кажется, что это немного упущение, я уверен, что будет несложно его поддержать, если не будет совпадений между DbContexts.
Конечно, решение должно быть модификацией команды EntityFramework, чтобы изменить API для поддержки прямого изменения таблицы _MigrationHistory на имя таблицы по вашему выбору, например _MigrationHistory_Context1, так, чтобы оно могло обрабатывать изменение независимых сущностей DbContext. Таким образом, все они обрабатываются отдельно, и разработчик должен убедиться, что имена объектов не конфликтуют.
Похоже, что есть много людей, которые разделяют мое мнение о том, что дублированный DbContext со ссылками на расширенный набор сущностей является фиктивным, не подходящим для предприятия способом решения проблем. Дублирование DbContexts с треском проваливается для модульных (Prism или аналогичных) решений.
Я хочу, чтобы люди знали, что ответ с этим ниже - то, что работало для меня, но с одним предостережением: не используйте строку MigrationsNamespace.
internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
public Configuration1 (){
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "YourProject.Models.ContextNamespace1";
}
}
internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
public Configuration2 (){
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "YourProject.Models.ContextNamespace2";
}
}
Однако у меня уже были установлены 2 базы данных с определенными их собственными контекстами, поэтому я обнаружил, что получаю сообщение об ошибке "Пространство имен YourProject.Models уже определило ContextNamespace1". Это произошло потому, что "MigrationsNamespace = "YourProject.Models.ContextNamespace2";" заставлял dbcontext быть определенным в пространстве имен YourProjects.Models дважды после того, как я попробовал Init (один раз в файле миграции context1Init и один раз там, где я его определил ранее).
Итак, я обнаружил, что в этот момент мне нужно было запустить мою базу данных и выполнить миграцию с нуля (к счастью, у меня не было данных, которые мне нужно было сохранить), следуя указаниям здесь: http://pawel.sawicz.eu/entity-framework-reseting-migrations/
Затем я изменил код, чтобы НЕ включать строку MigrationsNamespace.
internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
public Configuration1 (){
AutomaticMigrationsEnabled = false;
}
}
internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
public Configuration2 (){
AutomaticMigrationsEnabled = false;
}
}
Затем я снова запустил команду "Add-Migration -configuration Configuration1 Context1Init" и снова строку "Update-Database -configuration Configuration1" (для моего 2-го контекста), и, наконец, теперь все работает отлично.