问题
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?
回答1:
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.
回答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.
回答3:
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)
{ }
来源:https://stackoverflow.com/questions/6550046/using-mvc-mini-profiler-database-profiling-with-entity-framework-code-first