使用 mvc-mini-profiler 和 Entity Framework Code First 进行数据库分析
我在使用 ASP 构建的项目中使用 mvc-mini-profiler .Net MVC 3 和实体框架代码优先。
一切都运行良好,直到我尝试通过将连接包装在 ProfiledDbConnection
中(如文档中所述)来添加数据库分析。由于我使用的是 DbContext,因此我尝试提供连接的方式是通过使用静态工厂方法的构造函数:
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);
}
//...
}
当使用 ProfiledDbConnection
时,我收到以下错误:
ProviderInknownException:提供程序未返回 ProviderManifestToken 字符串。
堆栈跟踪:
[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
我已单步执行,ProfiledDbConnection.Get
返回的类型为 type ProfiledDbConnection
(即使当前的 MiniProfiler 为 null)。
在实例化 DbContext
之前,在全局 Application_BeginRequest()
方法中调用 MiniProfiler.Start()
方法。我还为每个请求调用 Start 方法,无论如何,但如果用户不处于正确的角色,则调用 stop :
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();
}
我不确定这是否会影响事情,但我也使用 StructureMap 作为 DbContext 的 IoC code> 使用以下初始化程序:
For<MyDbContext>().Singleton().HybridHttpOrThreadLocalScoped();
我知道这里有一个类似的问题,很好地解释了该用户所发生的情况,但它似乎并没有解决我的问题。
编辑:
为了清楚起见。我尝试将连接作为 ProfiledDbConnection
传递,以便分析从 Entity Framework Code First 生成的 sql。
实体框架期望类型为 SqlConnection
的连接,当然这不是。
这是我的连接字符串的示例(注意提供程序名称),
<add name="MyDbContext" connectionString="Server=.\SQLEXPRESS; Database=MyDatabase;Trusted_Connection=true;MultipleActiveResultSets=true" providerName="System.Data.SqlClient" />
我尝试创建自己的继承自 SqlConnection
的 ProfiledDbConnection
版本,但它是一个密封类。
如果有某种方法告诉实体框架有关自定义连接类型的信息,那么这也许会起作用。我尝试将连接字符串中的 providerName
设置为 MvcMiniProfiler.Data.ProfiledDbConnection
但这不起作用。
所以。也许问题的演变是:如何将自定义连接类型传递给实体框架代码优先?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
现在已完全支持此功能,请查看最新的源代码或从 nuget 获取软件包。
如果您使用 nuget,则需要 MiniProfiler.EF 包。 (1.9.1 及更高版本)
支持此功能需要对底层代理对象进行大量修改,以支持充当 EF 代码优先代理。
要添加此支持:
在
Application_Start
运行期间:MiniProfilerEF.Initialize();
注意:EF Code First 会将表元数据存储在名为的表中:
EdmMetadata
。此元数据使用提供程序作为实体键的一部分。如果您将提供程序初始化为非配置提供程序,则必须重新构建此元数据。从EdmMetadata
中删除所有行可能会解决问题,或者一些更聪明的提供商能够透明地处理这个问题。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 fromEdmMetadata
may do the trick, alternatively some smarter providers are able to handle this transparently.我仍然遇到问题使其工作,并发现我需要重命名或删除连接字符串才能使 Database.DefaultConnectionFactory 工作。
请参阅此答案更多细节。
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.
根据我的经验,此错误始终是无效的连接字符串,或者缺少与数据库的连接,例如“连接时发生网络服务错误...”。
另请注意,DbContext 只需要构造函数中的“connectionStringKey”,例如
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