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.
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?
This is now fully supported, check out the latest source or grab the package from nuget.
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.