我有一个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的精确副本,在那里我可以很好地访问我的数据库。
答案 0 :(得分:4)
原来Anders Abel在事业中是对的,但我们找到了一个更简单的解决方案。
根据mvc-mini-profiler page,Nuget中有一个名为MiniProfiler.EF
的特殊包,它不需要SqlConnection
周围的任何包装器。我们放弃了旧的mvc-mini-profiler并安装了MiniProfiler.EF
。然后Enable-Migrations
按预期工作。
答案 1 :(得分:3)
EF Code First具有用于Sql代码生成的可扩展提供程序模型。 DbMigrationsConfiguration.GetSqlGenerator
的文档说明了它的作用:
获取设置为与给定数据库一起使用的SQL生成器 提供商。
MvcMiniProfiler围绕数据库提供程序进行自我包装以添加性能分析支持。对于EF,它看起来像是在使用MvcMiniProfiler数据库,而不是MSSQL数据库。不幸的是,EF Code首先不知道如何处理MvcMiniProfiler数据库。
可能的解决方法是添加一个包含Sql Server生成器的MvcMiniProfiler名称的SqlGenerator。
看起来可能只需为mvc mini profiler名称重新注册现有的sql server generator(如果你找出它的名字)。
在http://romiller.com/2012/01/16/customizing-code-first-migrations-provider/,有代码段显示如何注册提供商:
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetSqlGenerator("System.Data.SqlClient",
new CustomMigrationsProviders.CustomSqlServerMigrationSqlGenerator());
}
答案 2 :(得分:0)
这可能是一个相关但不同的问题,但由于Anders的帖子引导我找到解决方案,我想我也在这里发布了这个解决方案。
问题:
如果在执行Entity Framework数据库初始化策略之前初始化了MiniProfiler,则初始化将失败,并显示有关缺少迁移表的错误。
如果实体框架数据库初始化策略首先执行,则对实体的访问会因类型转换异常而失败,因为尝试将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的确切版本。