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)
Alex
  • 7,901
  • 1
  • 41
  • 56
Daniel Lorenz
  • 4,178
  • 1
  • 32
  • 39
  • Can you post the exception you get? – Kyle Nunery Mar 16 '12 at 14:42
  • I added the exceptions to the main post. – Daniel Lorenz Mar 16 '12 at 14:56
  • 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? – Kyle Nunery Mar 18 '12 at 22:47
  • I'm using 1.9 as well. When I'm not using the profiler, I get data just fine with no crashes. – Daniel Lorenz Mar 19 '12 at 00:34
  • 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. – Kyle Nunery Mar 19 '12 at 02:34

4 Answers4

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.

Kyle Nunery
  • 2,048
  • 19
  • 23
  • I'm using MVC 4 with EF 4.3, which I mentioned in the title... Not MVC 3. – Daniel Lorenz Mar 19 '12 at 00:31
  • I didn't think MVC version had anything to do with this but I'll check. – Kyle Nunery Mar 19 '12 at 01:29
  • Awesome, this does work! I was just missing that static method call! Thanks! – Daniel Lorenz Mar 19 '12 at 13:55
  • 1
    Great, static constructor also fixed a problem I had with a Console App trying to connect to a EF code-first context – Adam Jun 17 '12 at 22:54
  • 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? – drzaus Jun 14 '13 at 15:26
  • 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. – Daniel Lorenz May 01 '14 at 21:17
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.

JoshRivers
  • 9,920
  • 8
  • 39
  • 39
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.

Efe Kaptan
  • 465
  • 2
  • 11
  • 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. – Daniel Lorenz Mar 17 '12 at 14:38
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

Rob
  • 4,927
  • 12
  • 49
  • 54
force
  • 1