使用 mvc-mini-profiler 和 Entity Framework Code First 进行数据库分析

发布于 2024-11-17 20:56:02 字数 4328 浏览 5 评论 0原文

我在使用 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。

Profiled Sql

实体框架期望类型为 SqlConnection 的连接,当然这不是。

这是我的连接字符串的示例(注意提供程序名称),

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

我尝试创建自己的继承自 SqlConnectionProfiledDbConnection 版本,但它是一个密封类。

如果有某种方法告诉实体框架有关自定义连接类型的信息,那么这也许会起作用。我尝试将连接字符串中的 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.

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

时光无声 2024-11-24 20:56:02

现在已完全支持此功能,请查看最新的源代码或从 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 from EdmMetadata may do the trick, alternatively some smarter providers are able to handle this transparently.

不乱于心 2024-11-24 20:56:02

我仍然遇到问题使其工作,并发现我需要重命名或删除连接字符串才能使 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.

去了角落 2024-11-24 20:56:02

根据我的经验,此错误始终是无效的连接字符串,或者缺少与数据库的连接,例如“连接时发生网络服务错误...”。

另请注意,DbContext 只需要构造函数中的“connectionStringKey”,例如

public MyDbContext() : 
     base("MyConnectionName", true)
    { }

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)
    { }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文