Miniprofiler 1.9.1 throws无法确定连接类型'System.Data.SqlClient.SqlConnection'与EF Code First的提供程序名称

时间:2011-09-06 09:59:52

标签: ef-code-first mvc-mini-profiler

我知道我可能有点厚......

首先使用EF代码创建一个新的MVC3测试应用程序。

上下文:

    public class EmployeeContext : DbContext
    {
        public DbSet<Employee> Employees { get; set; }
    }

控制器:

    public ActionResult Index()
    {
        List<Employee> employees;
        using (var ctx = new EmployeeContext())
        {
            employees = ctx.Employees.ToList();
        }
        return View(employees);
    }

    [HttpPost]
    public ActionResult Create(Employee employee)
    {
        using (var ctx = new EmployeeContext())
        {
            ctx.Employees.Add(employee);
            ctx.SaveChanges();
        }

        return RedirectToAction("Index");
    }

正如所料,EF创建了数据库,我可以创建并列出员工。

现在为miniprofiler。

从nuget添加miniprofiler.EF 1.9.1。

认为我只需要将以下行添加到global.asax application_start方法中:

    MiniProfilerEF.Initialize();

当我使用它运行时,我得到一个“无法确定连接类型'System.Data.SqlClient.SqlConnection'的提供程序名称。”异常。

我没有在web.config中添加任何东西吗?

3 个答案:

答案 0 :(得分:1)

不,你不厚:-)。 EF和MiniProfiler.EF 1.9.1似乎存在问题。见this question。显然,版本2.0中会有一种解决方法。

答案 1 :(得分:0)

答案 2 :(得分:0)

问题:

如果在执行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的确切版本。