Using Mini-Profilier with EF 4.3 & MVC 4 without creating the database

后端 未结 4 2035
不知归路
不知归路 2020-12-29 04:46

I have an issue where we are using EF 4.3 Code First against an existing database. I want to use the Mini-Profiler with EF and call

MvcMiniProfiler.MiniPro         


        
相关标签:
4条回答
  • 2020-12-29 04:57

    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 讨论(0)
  • 2020-12-29 05:11

    This exception occurs for me when i miss a setting for miniprofiler.

    Possible cases:

    1. Missing include in your layout head tag

      @MvcMiniProfiler.MiniProfiler.RenderIncludes()

    2. Missing "MiniProfiler.cs" in your App_Start folder.

    3. Missing call in Application_Start() function

      AreaRegistration.RegisterAllAreas();
      RegisterGlobalFilters(GlobalFilters.Filters);
      RegisterRoutes(RouteTable.Routes);
      
      BundleTable.Bundles.RegisterTemplateBundles();
      
      MiniProfilerEF.Initialize();
      

    Tested with mvc4, Ef 4.3 for existing database.

    0 讨论(0)
  • 2020-12-29 05:15

    I started a new MVC 4 project and installed/updated the following NuGet packages:

    • EntityFramework
    • MiniProfiler
    • MiniProfiler.EF

    I turned off the database initialization strategy in Code First inside of my database context.

    public class EmployeeContext : DbContext
    {
        static EmployeeContext()
        {
            Database.SetInitializer<EmployeeContext>( null ); // must be turned off before mini profiler runs
        }
    
        public IDbSet<Employee> Employees { get; set; } 
    }
    

    The mini profiler is working properly. I created the one table database by hand.

    Turning off the database initializer in the static constructor is important. If you do it elsewhere then it's possible that the mini profiler code runs before your code and hence the queries to the __MigrationHistory table that shouldn't be occurring at all.

    0 讨论(0)
  • 2020-12-29 05:21

    I found additional "hack" issue for disabling EntityFramework database initialization (if not required). DefaultInitializer for DB should be set to null before initializing db contexts and MiniProfiler

    Type type = typeof(DbContext).Assembly.GetType("System.Data.Entity.Internal.LazyInternalContext");
    var field = type.GetField("DefaultCodeFirstInitializer", BindingFlags.NonPublic | BindingFlags.Static);
    if (field != null)
        field.SetValue(null, null);
    else
    {
        var field2 = type.GetField("_defaultCodeFirstInitializer", BindingFlags.NonPublic | BindingFlags.Static);
        if (field2 != null)
            field2.SetValue(null, null);
    }
    

    So, it will resolve problems with dbo.EdmMetadata and dbo.__MigrationHistory tables

    0 讨论(0)
提交回复
热议问题