Подтвердить что ты не робот

Использование Mini-Profilier с EF 4.3 и MVC 4 без создания базы данных

У меня проблема, когда мы используем EF 4.3 Code First против существующей базы данных. Я хочу использовать Mini-Profiler с EF и вызывать

MvcMiniProfiler.MiniProfilerEF.Initialize();

Однако, поскольку мы фактически не создаем ни одну из таблиц, таблицы dbo.__ MigrationHistory и dbo.EdmMetadata не существуют. Профилировщик заканчивает сбой, потому что их не существует. Есть ли способ заставить профайлера игнорировать эти EF-коды? Первые конкретные таблицы? Спасибо!

EDIT:

Вот те исключения, которые я получаю: (Они приходят отдельно)

Invalid object name 'dbo.__MigrationHistory'.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at MvcMiniProfiler.Data.ProfiledDbCommand.ExecuteDbDataReader(CommandBehavior behavior) in \mvc-mini-profiler\MvcMiniProfiler\Data\ProfiledDbCommand.cs:line 155
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)



Invalid object name 'dbo.EdmMetadata'.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at MvcMiniProfiler.Data.ProfiledDbCommand.ExecuteDbDataReader(CommandBehavior behavior) in \mvc-mini-profiler\MvcMiniProfiler\Data\ProfiledDbCommand.cs:line 155
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)
4b9b3361

Ответ 1

Я начал новый проект MVC 4 и установил/обновил следующие пакеты NuGet:

  • EntityFramework
  • MiniProfiler
  • MiniProfiler.EF

Я отключил стратегию инициализации базы данных в Code First внутри моего контекста базы данных.

public class EmployeeContext : DbContext
{
    static EmployeeContext()
    {
        Database.SetInitializer<EmployeeContext>( null ); // must be turned off before mini profiler runs
    }

    public IDbSet<Employee> Employees { get; set; } 
}

Мини-профилер работает правильно. Я создал базу данных из одной таблицы вручную.

Важное значение имеет отключение инициализатора базы данных в статическом конструкторе. Если вы сделаете это в другом месте, то возможно, что код мини-профайлера работает до вашего кода и, следовательно, запросы к таблице __MigrationHistory, которая не должна встречаться вообще.

Ответ 2

Это исключение возникает для меня, когда я пропускаю настройку для минипрофилера.

Возможные случаи:

  • Отсутствует включение в тег заголовка макета

    @MvcMiniProfiler.MiniProfiler.RenderIncludes()

  • Отсутствует "MiniProfiler.cs" в папке App_Start.

  • Отсутствует вызов в функции Application_Start()

    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    
    BundleTable.Bundles.RegisterTemplateBundles();
    
    MiniProfilerEF.Initialize();
    

Протестировано с помощью mvc4, Ef 4.3 для существующей базы данных.

Ответ 3

Проблема:

Если MiniProfiler инициализирован до того, как будут выполнены стратегии инициализации базы данных Entity Framework, инициализация завершится ошибкой с отсутствующей таблицей переноса.

Если сначала выполняются стратегии инициализации базы данных Entity Framework, доступ к объектам выходит из строя с исключением исключения типа, поскольку MiniProfiler DbConnection пытается принудительно ввести в переменную SqlConnection (во внутреннем родовом формате).

Причина:

Когда MiniProfiler инициализируется, он использует отражение для извлечения коллекции поставщиков баз данных из частного статического поля в System.Data.Common.DbProviderFactories. Затем он переписывает этот список с помощью поставщиков прошивки MiniProfiler для замены локальных поставщиков. Это позволяет MiniProfiler беспрепятственно перехватывать любые вызовы в базу данных.

Когда Entity Framework инициализируется, он начинает компилировать модели данных и создавать кэшированные инициализированные базы данных, хранящиеся в System.Data.Entity.Internal.LazyInternalContext внутри некоторых частных статических полей. После их создания запросы к DbContext используют кэшированные модели и базы данных, которые внутренне типизированы для использования поставщиков, которые существовали во время инициализации.

Когда запускается стратегия инициализации базы данных Entity Framework, для правильного создания SQL для создания таблиц ему нужен доступ к первому собственному провайдеру Sql, а не к обойме MiniProfiler. Но как только эти вызовы для собственного провайдера сделаны, собственный провайдер кэшируется в LazyInternalContext, и мы больше не можем вводить прокладки MiniProfiler без сбоев во время выполнения.

Мое решение:

Доступ к частным коллекциям внутри System.Data.Entity.Internal.LazyInternalContext и очистка кэшированных скомпилированных моделей и инициализированных баз данных.

Если я выполняю эту очистку между действиями стратегий инициализации базы данных EF и инициализацией MiniProfiler, тогда можно будет вставить прокладки MiniProfiler без последующего сбоя в работе.

Код: Этот код помогло:

Type type = typeof(DbContext).Assembly.GetType("System.Data.Entity.Internal.LazyInternalContext");
object concurrentDictionary = (type.GetField("InitializedDatabases", BindingFlags.NonPublic | BindingFlags.Static)).GetValue(null);
var initializedDatabaseCache = (IDictionary)concurrentDictionary;
if (initializedDatabaseCache != null) initializedDatabaseCache.Clear();
object concurrentDictionary2 = (type.GetField("CachedModels", BindingFlags.NonPublic | BindingFlags.Static)).GetValue(null);
var modelsCache = (IDictionary)concurrentDictionary2;
if (modelsCache != null) modelsCache.Clear();

Внимание:

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

Ответ 4

Я обнаружил дополнительную проблему "взлома" для отключения инициализации базы данных EntityFramework (если это не требуется). DefaultInitializer для DB должен быть установлен в значение null до инициализации контекстов db и MiniProfiler

Type type = typeof(DbContext).Assembly.GetType("System.Data.Entity.Internal.LazyInternalContext");
var field = type.GetField("DefaultCodeFirstInitializer", BindingFlags.NonPublic | BindingFlags.Static);
if (field != null)
    field.SetValue(null, null);
else
{
    var field2 = type.GetField("_defaultCodeFirstInitializer", BindingFlags.NonPublic | BindingFlags.Static);
    if (field2 != null)
        field2.SetValue(null, null);
}

Таким образом, он разрешит проблемы с таблицами dbo.EdmMetadata и dbo.__MigrationHistory