24

I'm using the mvc-mini-profiler in my project built with ASP.Net MVC 3 and Entity Framework code-first.

Everything works great until I attempt to add database profiling by wrapping the connection in the ProfiledDbConnection as described in the documentation. Since I'm using a DbContext, the way I am attempting to provide the connection is through the constructor using a static factory method:

public class MyDbContext : DbContext
{                
    public MyDbContext() : base(GetProfilerConnection(), true)
    { }

    private static DbConnection GetProfilerConnection()
    {
        // Code below errors
        //return ProfiledDbConnection.Get(new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionName"].ConnectionString));

        // Code below works fine...
        return new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionName"].ConnectionString);
    }

    //...
}

When using the ProfiledDbConnection, I get the following error:

ProviderIncompatibleException: The provider did not return a ProviderManifestToken string.

Stack Trace:

[ArgumentException: The connection is not of type 'System.Data.SqlClient.SqlConnection'.]
   System.Data.SqlClient.SqlProviderUtilities.GetRequiredSqlConnection(DbConnection connection) +10486148
   System.Data.SqlClient.SqlProviderServices.GetDbProviderManifestToken(DbConnection connection) +77
   System.Data.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection) +44

[ProviderIncompatibleException: The provider did not return a ProviderManifestToken string.]
System.Data.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection) +11092901
   System.Data.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection) +11092745
   System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection) +221
   System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext) +61
   System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input) +1203482
   System.Data.Entity.Internal.LazyInternalContext.InitializeContext() +492
   System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) +26
   System.Data.Entity.Internal.Linq.InternalSet`1.Initialize() +89
   System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext() +21
   System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider() +44
   System.Linq.Queryable.Where(IQueryable`1 source, Expression`1 predicate) +135

I have stepped through and the type returned by ProfiledDbConnection.Get is of type ProfiledDbConnection (Even if the current MiniProfiler is null).

The MiniProfiler.Start() method is called within the Global Application_BeginRequest() method before the DbContext is instantiated. I am also calling the Start method for every request regardless but calling stop if the user is not in the correct role:

    protected void Application_BeginRequest()
    {
        // We don't know who the user is at this stage so need to start for everyone
        MiniProfiler.Start();
    }

    protected void Application_AuthorizeRequest(Object sender, EventArgs e)
    {
        // Now stop the profiler if the user is not a developer
        if (!AuthorisationHelper.IsDeveloper())
        {
            MvcMiniProfiler.MiniProfiler.Stop(discardResults: true);
        }
    }

    protected void Application_EndRequest()
    {
        MiniProfiler.Stop();
    }

I'm not sure if this affects things but I'm also using StructureMap as IoC for the DbContext using the following initialiser:

For<MyDbContext>().Singleton().HybridHttpOrThreadLocalScoped();

I understand that there is a similar question on here with a good explanation of what's happening for that user, however it doesn't seem to solve my problem.

EDIT:

For clarity. I am attempting to pass the connection as ProfiledDbConnection in order to profile the generated sql from Entity Framework Code First.

Profiled Sql

The Entity Framework is expecting a connection with type SqlConnection which of course this isn't.

Here is an example of my connection string (notice the providerName)

<add name="MyDbContext" connectionString="Server=.\SQLEXPRESS; Database=MyDatabase;Trusted_Connection=true;MultipleActiveResultSets=true" providerName="System.Data.SqlClient" />

I attempted to create my own version of the ProfiledDbConnection inheriting from SqlConnection but it is a sealed class.

If there is some way of telling Entity Framework about the custom connection type then perhaps this would work. I tried setting the providerName in the connection string to MvcMiniProfiler.Data.ProfiledDbConnection but that didn't work.

So. Perhaps an evolution of the question would be: How can you pass a custom connection type to Entity Framework Code First?

robmzd
  • 1,813
  • 3
  • 20
  • 37

3 Answers3

13

This is now fully supported, check out the latest source or grab the package from nuget.

You will need the MiniProfiler.EF package if you are using nuget. (1.9.1 and up)

Supporting this involved a large set of modifications to the underlying proxy object to support acting as EF code first proxies.

To add this support:

During your Application_Start run:

MiniProfilerEF.Initialize();

Note: EF Code First will store table metadata in a table called: EdmMetadata. This metadata uses the provider as part of the entity key. If you initialized your provider as a non-profiled provider, you will have to re-build this metadata. Deleting all the rows from EdmMetadata may do the trick, alternatively some smarter providers are able to handle this transparently.

Community
  • 1
  • 1
Sam Saffron
  • 128,308
  • 78
  • 326
  • 506
  • Sam you're a superstar! Thanks for the fix – robmzd Jul 19 '11 at 10:19
  • 1
    Does this method require the EdmMetadata table? We are using Code First with a database not created by EF. I implemented the method above (though with normal SQL Server, as described on Scott Hanselman's blog), but SQL queries are not showing up in the profiler. – avesse Jul 22 '11 at 14:39
  • 1
    @avesse Have you checked that you are not hitting [this problem](http://stackoverflow.com/q/6757659/859295) – Chris Foster Jul 26 '11 at 06:13
  • 2
    Perhaps you should mention that the remove and add nodes in the web.config should be added under then – Per Hornshøj-Schierbeck Aug 16 '11 at 20:25
  • 1
    Your answer assumes too much knowledge about EF and MiniProfiler and/or is out of date. As a newb to both libraries, I'm little better off having read your answer. As @Per suggested, please indicate where in web.config that setting goes. Also, please indicate possibilities where the code snippet should go. App start? Is this what comes in the MiniProfiler.cs file? If so, then point that out too. – RyanW Aug 26 '11 at 19:20
  • @RyanW I would chuck the code in app start, but it is totally up to you. added the location – Sam Saffron Aug 28 '11 at 11:38
  • @Sam can you please clarify if you need to keep the previous code `MiniProfiler.Start` or replace it entirely with `MiniProfilerEF.Initialize()`. BTW does 1.9 work with MVC 2 database first method? – JK. Oct 21 '11 at 02:30
  • @JK. initialize and start are unrelated, one is related to initialization (called once per app lifecycle) ... the other to the profiling session in progress (one per request) – Sam Saffron Oct 21 '11 at 02:32
  • Thanks - just that your answers only show the new `MiniProfilerEF.Initialize()` line, so I was not sure if we only needed one or needed both. – JK. Oct 21 '11 at 02:36
  • Is there a way to use this when you do not generate the database via Code First. I created Entities based upon an existing database. – Daniel Lorenz Mar 15 '12 at 21:19
2

I was still having problems getting this to work and found that I needed to rename or remove the connection string to get Database.DefaultConnectionFactory to work.

Please refer to this answer for more detail.

Community
  • 1
  • 1
Chris Foster
  • 1,310
  • 11
  • 13
-1

This error in my experience has always been an invalid connection string, or a lack of connection to the DB, like "A network service error occurred while connecting...".

Also note that the DbContext just needs the "connectionStringKey" in the constructor, like

public MyDbContext() : 
     base("MyConnectionName", true)
    { }
Dominic Zukiewicz
  • 8,258
  • 8
  • 43
  • 61
  • Thanks, this will get rid of the exception, however what I am attempting is to pass the connection as an instance of the ProfiledDbConnection in order to profile the sql. – robmzd Jul 13 '11 at 07:33