Не удается включить миграцию в 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, которую вы включили в свой проект.

Другие вопросы по тегам