无法在Entity Framework 4.3中启用迁移

4

我有一个包含EF Code First的类库。我刚刚升级到EF 4.3,现在我想启用迁移。

我在PM控制台中输入Enable-Migrations -ProjectName MyProjectName,但是收到以下错误信息:

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完全相同的副本,在那里我可以完美地访问我的数据库。


看起来 MvcMiniProfiler 可能是罪魁祸首。移除它后,就可以启用迁移了。 - Albin Sunnanbo
3个回答

4
Anders Abel的想法是正确的,但我们找到了一个更简单的解决方案。
根据mvc-mini-profiler页面,Nuget中有一个特殊的包叫做MiniProfiler.EF,不需要在SqlConnection周围添加任何包装器。我们放弃了旧的mvc-mini-profiler并安装了MiniProfiler.EF。然后Enable-Migrations按预期工作。

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());
}

0

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

问题:

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

如果Entity Framework数据库初始化策略先执行,则访问实体时会出现类型转换异常,因为MiniProfiler DbConnection试图被强制转换为SqlConnection变量(在内部泛型中)。

原因:

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

当Entity Framework初始化时,它开始编译数据模型并创建存储在System.Data.Entity.Internal.LazyInternalContext中的缓存初始化数据库,存储在一些私有静态字段中。一旦创建了这些内容,DbContext的查询将使用缓存的模型和数据库,这些模型和数据库在初始化时内部类型化以使用存在的提供程序。

当实体框架数据库初始化策略运行时,它需要访问裸的、原生的 Sql 提供程序,而不是 MiniProfiler shim,以便正确生成 SQL 来创建表。但一旦这些对原生提供程序的调用被执行,原生提供程序就会被缓存到 LazyInternalContext 中,我们就无法在运行时注入 MiniProfiler shims,否则会导致运行时失败。

我的解决方案:

访问 System.Data.Entity.Internal.LazyInternalContext 内部的私有集合,并清除缓存的编译模型和已初始化的数据库。

如果我在 EF 数据库初始化策略的操作和 MiniProfiler 的初始化之间执行此清除操作,则可以插入 MiniProfiler shims 而不会导致后续运行时失败。

代码: 以下代码对我很有帮助:

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 兼容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接