4

我有一个带有 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 的精确副本,在那里我可以很好地访问我的数据库。

4

3 回答 3

4

事实证明 Anders Abel 是正确的,但我们找到了一个更简单的解决方案。

根据mvc-mini-profiler 页面,在 Nuget 中有一个名为的特殊包,MiniProfiler.EF它不需要围绕SqlConnection. 我们放弃了旧的 mvc-mini-profiler 并安装了MiniProfiler.EF. 然后Enable-Migrations按预期工作。

于 2012-03-09T07:22:44.057 回答
3

EF Code First 具有用于 Sql 代码生成的可扩展提供程序模型。的文档DbMigrationsConfiguration.GetSqlGenerator说明了它的作用:

获取设置为与给定数据库提供程序一起使用的 SQL 生成器。

MvcMiniProfiler 将自身包装在 DB 提供程序周围以添加分析支持。对于 EF,它看起来就像您使用的是 MvcMiniProfiler DB 而不是 MSSQL DB。不幸的是,EF Code first 不知道如何处理 MvcMiniProfiler DB。

一个可能的解决方法是添加一个带有 MvcMiniProfiler 名称的 SqlGenerator,该名称包装了 Sql Server 生成器。

编辑

看起来可以只为 mvc mini profiler 名称重新注册现有的 sql server 生成器(如果您知道它的名称)。

http://romiller.com/2012/01/16/customizing-code-first-migrations-provider/有显示如何注册提供者的代码片段:

public Configuration()
{
    AutomaticMigrationsEnabled = false;

    SetSqlGenerator("System.Data.SqlClient", 
        new CustomMigrationsProviders.CustomSqlServerMigrationSqlGenerator());
}
于 2012-03-08T17:41:46.673 回答
0

这可能是一个相关但不同的问题,但由于是 Anders 的帖子让我找到了解决方案,我想我也在这里发布了这个解决方案。

问题:

如果在我们的 Entity Framework 数据库初始化策略执行之前初始化 MiniProfiler,则初始化会失败,并出现有关缺少迁移表的错误。

如果首先执行 Entity Framework 数据库初始化策略,则对实体的访问将失败并出现类型转换异常,因为 MiniProfiler DbConnection 被尝试强制进入 SqlConnection 变量(在内部泛型中)。

原因:

当 MiniProfiler 初始化时,它使用反射从 System.Data.Common.DbProviderFactories 的私有静态字段中检索数据库提供程序的集合。然后,它使用 MiniProfiler shim 提供程序重写此列表以替换本机提供程序。这允许 MiniProfiler 以静默方式拦截对数据库的任何调用。

当实体框架初始化时,它开始编译数据模型并创建缓存的初始化数据库,存储在一些私有静态字段内的 System.Data.Entity.Internal.LazyInternalContext 中。创建这些后,针对 DbContext 的查询将使用缓存模型和数据库,这些模型和数据库在内部键入以使用初始化时存在的提供程序。

当 Entity Framework 数据库初始化策略运行时,它需要访问裸机的原生 Sql 提供程序,而不是 MiniProfiler shim,以便正确生成 SQL 以创建表。但是一旦对原生提供者进行了这些调用,原生提供者就会被缓存到 LazyInternalContext 中,并且我们不能再注入 MiniProfiler shims 而不会出现运行时故障。

我的解决方案:

访问 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。

于 2013-04-18T17:20:59.613 回答