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

4

24

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.MiniProfilerEF.Initialize();

However, since we don't actually create any of the tables, the dbo.__MigrationHistory and dbo.EdmMetadata tables do not exist. The profiler ends up crashing because they don't exist. Is there any way to make the profiler ignore these EF Code First specific tables? Thanks!

EDIT:

These are the exceptions I get: (They come separately)

Invalid object name 'dbo.__MigrationHistory'.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at MvcMiniProfiler.Data.ProfiledDbCommand.ExecuteDbDataReader(CommandBehavior behavior) in \mvc-mini-profiler\MvcMiniProfiler\Data\ProfiledDbCommand.cs:line 155
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)



Invalid object name 'dbo.EdmMetadata'.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at MvcMiniProfiler.Data.ProfiledDbCommand.ExecuteDbDataReader(CommandBehavior behavior) in \mvc-mini-profiler\MvcMiniProfiler\Data\ProfiledDbCommand.cs:line 155
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)
Purree answered 2/3, 2012 at 20:32 Comment(5)
Can you post the exception you get?Brezin
I added the exceptions to the main post.Purree
What version of MVC Mini Profiler are you running? I used 1.9 in my testing and it worked. It seems these errors are not related to the profiler. What code did you use to turn off the conventions in Code First that causes Code First to look at these tables?Brezin
I'm using 1.9 as well. When I'm not using the profiler, I get data just fine with no crashes.Purree
What code did you use to turn off the conventions in Code First that causes Code First to look at these tables? These tables should not be queried at all once you properly configure Code First to handle an existing database.Brezin
B
27

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.

Brezin answered 18/3, 2012 at 22:44 Comment(6)
I'm using MVC 4 with EF 4.3, which I mentioned in the title... Not MVC 3.Purree
I didn't think MVC version had anything to do with this but I'll check.Brezin
Awesome, this does work! I was just missing that static method call! Thanks!Purree
Great, static constructor also fixed a problem I had with a Console App trying to connect to a EF code-first contextLagting
Is this only applicable to working with profilers? (I'm getting a similar problem with HibernatingRhino's profiler). Is this something you only want to do in development code? i.e. are there any side effects?Joanajoane
Heh, way late on this but: Setting that SetInitializer to null tells EF that we don't want EF managing the database for us. Thus, this means it will ignore those EF specific tables. There is nothing bad about using it unless you want code first to generate the database for you.Purree
W
1

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.

Welldefined answered 17/3, 2012 at 8:41 Comment(1)
Nope, this did not fix the problem. I had all of that in the code already. This is a database that Code First NEVER generated and will NEVER generate. Thus, these 2 tables do not exist in this database.Purree
D
1

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.

Discomfortable answered 18/4, 2013 at 17:25 Comment(0)
S
0

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

Smell answered 11/6, 2013 at 8:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.