Не удается включить миграцию в Entity Framework 4.3
У меня есть библиотека классов с EF Code First. Я только что обновился до EF 4.3 и теперь хочу включить миграцию.
Я печатаю Enable-Migrations -ProjectName MyProjectName
в консоли PM, но получите следующую ошибку
PM> Enable-Migrations -ProjectName MyProjectName
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at System.Data.Entity.Migrations.DbMigrationsConfiguration.GetSqlGenerator(String providerInvariantName)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
at System.Data.Entity.Migrations.Design.MigrationScaffolder..ctor(DbMigrationsConfiguration migrationsConfiguration)
at System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldRunner.RunCore()
at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()
The given key was not present in the dictionary.
PM>
Я не могу понять, в каком словаре это может быть неправильно.
Моя строка подключения выглядит так:
<connectionStrings>
<add name="MySystem" connectionString="Data Source=MyServer\Instance;Initial Catalog=myDbName;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
Есть идеи о том, что может быть не так?
Просто примечание:
Я использую свою библиотеку классов в консольном приложении с точной копией app.config, и там я могу получить доступ к своей базе данных на отлично.
3 ответа
Оказалось, что Андерс Абель был прав в этом деле, но мы нашли гораздо более простое решение.
Согласно странице mvc-mini-profiler, в Nuget есть специальный пакет, который называется MiniProfiler.EF
это не требует никакой обертки вокруг SqlConnection
, Мы бросили наш старый MVC-мини-профилировщик и установили MiniProfiler.EF
, затем Enable-Migrations
работал как положено.
EF Code First имеет расширяемую модель провайдера для генерации кода Sql. Документация для DbMigrationsConfiguration.GetSqlGenerator
говорит, что он делает:
Получает генератор SQL, который настроен для использования с данным поставщиком базы данных.
MvcMiniProfiler оборачивается вокруг провайдера БД, чтобы добавить поддержку профилирования. Для EF это будет выглядеть так, как будто вы используете БД MvcMiniProfiler, а не БД MSSQL. К сожалению, EF Code сначала не знает, как обращаться с БД MvcMiniProfiler.
Возможное исправление заключается в добавлении SqlGenerator с именем MvcMiniProfiler, который оборачивает генератор Sql Server.
редактировать
Похоже, что можно просто перерегистрировать существующий генератор сервера sql для имени мини-профилировщика mvc (если вы выясните его имя).
На http://romiller.com/2012/01/16/customizing-code-first-migrations-provider/ есть фрагмент кода, который показывает, как зарегистрировать провайдера:
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetSqlGenerator("System.Data.SqlClient",
new CustomMigrationsProviders.CustomSqlServerMigrationSqlGenerator());
}
Это может быть связанная, но другая проблема, но так как именно пост Андерса привел меня к решению, я решил опубликовать это решение и здесь.
Эта проблема:
Если MiniProfiler инициализируется до выполнения наших стратегий инициализации базы данных Entity Framework, инициализация завершается ошибкой с ошибкой в отсутствующей таблице миграции.
Если стратегии инициализации базы данных Entity Framework выполняются первыми, доступ к объектам завершается неудачно с исключением приведения типа, так как MiniProfiler DbConnection пытаются принудительно принудительно ввести в переменную SqlConnection (во внутреннем универсальном).
Причина:
Когда MiniProfiler инициализируется, он использует отражение для извлечения коллекции поставщиков базы данных из частного статического поля в System.Data.Common.DbProviderFactories. Затем он переписывает этот список с поставщиками прокладок MiniProfiler для замены собственных поставщиков. Это позволяет MiniProfiler молча перехватывать любые вызовы базы данных.
Когда Entity Framework инициализируется, он начинает компилировать модели данных и создавать кэшированные инициализированные базы данных, хранящиеся в System.Data.Entity.Internal.LazyInternalContext, в некоторых закрытых статических полях. Как только они созданы, запросы к DbContext используют кэшированные модели и базы данных, которые внутренне типизированы для использования поставщиков, которые существовали во время инициализации.
Когда запускается стратегия инициализации базы данных Entity Framework, ей нужен доступ к чистому, родному провайдеру Sql, а не коду MiniProfiler, чтобы правильно генерировать SQL для создания таблиц. Но как только эти обращения к собственному провайдеру сделаны, нативный провайдер кэшируется в LazyInternalContext, и мы больше не можем вставлять прокладки MiniProfiler без сбоев во время выполнения.
Мое решение:
Получите доступ к частным коллекциям внутри System.Data.Entity.Internal.LazyInternalContext и очистите кешированные скомпилированные модели и инициализированные базы данных.
Если я выполню эту очистку между работой стратегий инициализации базы данных EF и инициализацией MiniProfiler, тогда вставки прокладок MiniProfiler могут быть вставлены, не вызывая при этом последующих сбоев во время выполнения.
Код: этот код помог мне:
Type type = typeof(DbContext).Assembly.GetType("System.Data.Entity.Internal.LazyInternalContext");
object concurrentDictionary = (type.GetField("InitializedDatabases", BindingFlags.NonPublic | BindingFlags.Static)).GetValue(null);
var initializedDatabaseCache = (IDictionary)concurrentDictionary;
if (initializedDatabaseCache != null) initializedDatabaseCache.Clear();
object concurrentDictionary2 = (type.GetField("CachedModels", BindingFlags.NonPublic | BindingFlags.Static)).GetValue(null);
var modelsCache = (IDictionary)concurrentDictionary2;
if (modelsCache != null) modelsCache.Clear();
Предупреждение:
Похоже, что имена внутренних полей в LazyInternalContext изменяются между версиями EF, поэтому вам может потребоваться изменить этот код для работы с точной версией EF, которую вы включили в свой проект.