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?

4

3 に答える 3

13

これは完全にサポートされるようになりました。最新のソースを確認するか、nuget からパッケージを入手してください。

nuget を使用している場合は、MiniProfiler.EF パッケージが必要になります。(1.9.1 以降)

これをサポートするには、EF コードの最初のプロキシとしての動作をサポートするために、基になるプロキシ オブジェクトに大規模な変更を加える必要がありました。

このサポートを追加するには:

Application_Startランニング中:

MiniProfilerEF.Initialize();

: EF Code First は、テーブル メタデータを次の名前のテーブルに格納しますEdmMetadata。このメタデータは、プロバイダーをエンティティ キーの一部として使用します。プロファイリングされていないプロバイダーとしてプロバイダーを初期化した場合は、このメタデータを再構築する必要があります。からすべての行を削除EdmMetadataするとうまくいくかもしれませんが、代わりに、一部のよりスマートなプロバイダーはこれを透過的に処理できます。

于 2011-07-19T07:56:46.393 に答える
2

これを機能させるにはまだ問題があり、Database.DefaultConnectionFactory を機能させるには、接続文字列の名前を変更するか削除する必要があることがわかりました。

詳細については、この回答を参照してください。

于 2011-07-23T13:53:50.260 に答える
-1

私の経験では、このエラーは常に無効な接続文字列、または「接続中にネットワークサービスエラーが発生しました...」のようなDBへの接続の欠如でした。

また、DbContextには、コンストラクターに「connectionStringKey」が必要なだけであることに注意してください。

public MyDbContext() : 
     base("MyConnectionName", true)
    { }
于 2011-07-12T21:02:31.267 に答える