开发者

Miniprofiler 1.9.1 throws Unable to determine the provider name for connection of type 'System.Data.SqlClient.SqlConnection' with EF Code First

开发者 https://www.devze.com 2023-04-03 11:16 出处:网络
I know I am probably being a bit thick however...... Created a new MVC3 test application using EF code first.

I know I am probably being a bit thick however......

Created a new MVC3 test application using EF code first.

Context:

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

Controller:

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

As expected, EF creates the database and I can create and list an employee.

Now for miniprofiler.

Added miniprofiler.EF 1.9.1 from nuget.

I think i just need to add the following line to the global.asax application_start method:

    MiniProfilerEF.Initialize();

When I run using this, I get a "Unable to determine the provider name for connection of type 'System.Data.SqlClient.SqlConnection'." exception.

I do not have to add anything to the web.config right?


No, you're not thick :-). There appears to be problem with EF and MiniProfiler.EF 1.9.1. See this question. Apparently, there will a workaround in version 2.0.


There is a currently-unresolved issue open tracking this:

http://code.google.com/p/mvc-mini-profiler/issues/detail?id=112&q=Unable%20to%20determine%20the%20provider%20name


The Problem:

If MiniProfiler is initialized before our Entity Framework database initialization strategies execute, the initialization fails with an error about a missing migration table.

If the Entity Framework database initialization strategies execute first, access to entities fails with a type casting exception as the MiniProfiler DbConnection is tried to be forced into a SqlConnection variable (in an internal generic).

The Cause:

When MiniProfiler initializes, it uses reflection to retrieve a collection of database providers from a private static field in System.Data.Common.DbProviderFactories. It then rewrites this list with MiniProfiler shim providers to replace the native providers. This allows MiniProfiler to intercept any calls to the database silently.

When Entity Framework initializes, it starts to compile the data models and create cached initialized databases stored in System.Data.Entity.Internal.LazyInternalContext inside some private static fields. Once these are created, queries against the DbContext use the cached models and databases which are internally typed to use the providers that existed at initialization time.

When the Entity Framework database initialization strategy runs, it needs access to the bare, native Sql provider, not the MiniProfiler shim, in order to correctly generate the SQL to create tables. But once these calls to the native provider are made, the native provider is cached into LazyInternalContext and we can no longer inject the MiniProfiler shims without runtime failures.

My Solution:

Access the private collections inside System.Data.Entity.Internal.LazyInternalContext and clear out the cached compiled models and initialized databases.

If I perform this purge between the operation of the EF database initialization strategies and the initialization of MiniProfiler, the MiniProfiler shims can then be inserted without causing later runtime failures.

Code: This code did the trick for me:

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

Warning:

It appears that the names of the internal fields in LazyInternalContext change between versions of EF, so you may need to modify this code to work with the exact version of EF that you include in your project.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号