Использование профилирования базы данных mvc-mini-profiler с Entity Framework Code First

Я использую mvc-mini-profiler в своем проекте, созданном с помощью ASP. .Net MVC 3 и Entity Framework в первую очередь.

Все работает отлично, пока я не попытаюсь добавить профилирование базы данных, заключив соединение в 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 я получаю следующую ошибку:

ProviderIncompatibleException: The provider did not return a ProviderManifestToken string.

Трассировки стека:

[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, имеет тип ProfiledDbConnection (даже если текущий MiniProfiler имеет значение null).

Метод MiniProfiler.Start() вызывается в методе Global Application_BeginRequest() перед созданием экземпляра DbContext. Я также вызываю метод 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 как IoC для DbContext, используя следующий инициализатор:

For<MyDbContext>().Singleton().HybridHttpOrThreadLocalScoped();

Я понимаю, что здесь есть аналогичный вопрос с хорошим объяснением того, что происходит для этого пользователя, однако, похоже, это не решает мою проблему.

ИЗМЕНИТЬ:

Для ясности. Я пытаюсь передать соединение как ProfiledDbConnection, чтобы сначала профилировать сгенерированный sql из кода Entity Framework.

Профилированный SQL

Entity Framework ожидает соединение с типом SqlConnection, что, конечно же, не так.

Вот пример моей строки подключения (обратите внимание на providerName)

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

Я попытался создать свою собственную версию ProfiledDbConnection, унаследованную от SqlConnection, но это закрытый класс.

Если есть какой-то способ сообщить Entity Framework о пользовательском типе подключения, возможно, это сработает. Я попытался установить providerName в строке подключения на MvcMiniProfiler.Data.ProfiledDbConnection, но это не сработало.

Так. Возможно, эволюция вопроса будет заключаться в следующем: как вы можете передать настраиваемый тип соединения в Entity Framework Code First?


person robmzd    schedule 01.07.2011    source источник


Ответы (3)


Теперь это полностью поддерживается, проверьте последний исходный код или возьмите пакет из nuget.

Вам понадобится пакет MiniProfiler.EF, если вы используете nuget. (1.9.1 и выше)

Для поддержки этого потребовался большой набор модификаций базового прокси-объекта для поддержки работы в качестве первых прокси-серверов кода EF.

Чтобы добавить эту поддержку:

Во время Application_Start пробежки:

MiniProfilerEF.Initialize();

Примечание. EF Code First будет хранить метаданные таблицы в таблице с именем: EdmMetadata. Эти метаданные используют поставщика как часть ключа сущности. Если вы инициализировали своего провайдера как непрофильного провайдера, вам придется перестроить эти метаданные. Удаление всех строк из EdmMetadata может помочь, в качестве альтернативы некоторые более умные провайдеры могут обрабатывать это прозрачно.

person Sam Saffron    schedule 19.07.2011
comment
Сэм, ты суперзвезда! Спасибо за исправление - person robmzd; 19.07.2011
comment
Требуется ли для этого метода таблица EdmMetadata? Мы используем Code First с базой данных, созданной не EF. Я применил описанный выше метод (хотя и с обычным SQL Server, как описано в блоге Скотта Хансельмана), но SQL-запросы не отображаются в профилировщике. - person avesse; 22.07.2011
comment
@avesse Вы проверили, что не сталкиваетесь с этой проблемой - person Chris Foster; 26.07.2011
comment
Возможно, вам следует упомянуть, что узлы удаления и добавления в web.config должны быть добавлены в ‹system.data›, а затем ‹DbProviderFactories› - person Per Hornshøj-Schierbeck; 17.08.2011
comment
Ваш ответ предполагает слишком много знаний об EF и MiniProfiler и/или устарел. Как новичок в обеих библиотеках, мне немного лучше прочитать ваш ответ. Как предложил @Per, укажите, где в web.config находится этот параметр. Кроме того, пожалуйста, укажите варианты, куда следует поместить фрагмент кода. Запуск приложения? Это то, что содержится в файле MiniProfiler.cs? Если да, то укажите это тоже. - person RyanW; 26.08.2011
comment
@RyanW Я бы выбросил код при запуске приложения, но это полностью зависит от вас. добавил место - person Sam Saffron; 28.08.2011
comment
@ Сэм, не могли бы вы уточнить, нужно ли вам сохранить предыдущий код MiniProfiler.Start или полностью заменить его на MiniProfilerEF.Initialize(). Кстати, 1.9 работает с первым методом базы данных MVC 2? - person JK.; 21.10.2011
comment
@JK. инициализация и запуск не связаны, один связан с инициализацией (вызывается один раз за жизненный цикл приложения) ... другой с текущим сеансом профилирования (по одному на запрос) - person Sam Saffron; 21.10.2011
comment
Спасибо - просто ваши ответы показывают только новую строку MiniProfilerEF.Initialize(), поэтому я не был уверен, нужна ли нам только одна или обе. - person JK.; 21.10.2011
comment
Есть ли способ использовать это, когда вы не создаете базу данных через Code First. Я создал сущности на основе существующей базы данных. - person Daniel Lorenz; 16.03.2012

У меня все еще были проблемы с тем, чтобы заставить это работать, и я обнаружил, что мне нужно переименовать или удалить строку подключения, чтобы заставить Database.DefaultConnectionFactory работать.

См. этот ответ для подробнее.

person Chris Foster    schedule 23.07.2011

По моему опыту, эта ошибка всегда представляла собой недопустимую строку подключения или отсутствие подключения к БД, например «При подключении произошла ошибка сетевой службы...».

Также обратите внимание, что DbContext просто нуждается в «connectionStringKey» в конструкторе, например

public MyDbContext() : 
     base("MyConnectionName", true)
    { }
person Dominic Zukiewicz    schedule 12.07.2011
comment
Спасибо, это избавит от исключения, однако я пытаюсь передать соединение как экземпляр ProfiledDbConnection, чтобы профилировать sql. - person robmzd; 13.07.2011