MVC Mini Profiler с Microsoft Enterprise Library - PullRequest
9 голосов
/ 17 августа 2011

Я уверен, что можно профилировать SQL-команды Enterprise Library, но я не смог понять, как обернуть соединение. Вот что я придумал:

Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand(PROC);

ProfiledDbCommand cmd = new ProfiledDbCommand(dbCommand, dbCommand.Connection, MvcMiniProfiler.MiniProfiler.Current);
db.AddInParameter(cmd, "foo", DbType.Int64, 0);

DataSet ds = db.ExecuteDataSet(cmd);

Это приводит к следующему исключению:

Невозможно привести объект типа «MvcMiniProfiler.Data.ProfiledDbCommand» к типу «System.Data.SqlClient.SqlCommand».

Ответы [ 3 ]

12 голосов
/ 27 августа 2011

Исключение происходит из этой строки в базе данных Entlib. DoLoadDataSet

    ((IDbDataAdapter) adapter).SelectCommand = command; 

В этом случае адаптер имеет тип SqlDataAdapter и ожидает SqlCommand, а команда, созданная ProfiledDbProviderFactory, имеет тип ProfiledDbCommand, как вы видите в исключении.

Это решение предоставит EntLib общий DbDataAdapter, переопределив CreateDataAdapter и CreateCommand в ProfiledDbProviderFactory. Кажется, это работает как надо, но я прошу прощения, если я видел какие-либо нежелательные последствия, которые этот хак мог иметь (или воспаленные глаза, которые он мог вызвать;). Вот оно:

  1. Создание двух новых классов ProfiledDbProviderFactoryForEntLib и DbDataAdapterForEntLib

    public class ProfiledDbProviderFactoryForEntLib : ProfiledDbProviderFactory
    {
        private DbProviderFactory _tail;
        public static ProfiledDbProviderFactory Instance = new ProfiledDbProviderFactoryForEntLib();
    
        public ProfiledDbProviderFactoryForEntLib(): base(null, null)
        {
        }
    
        public void InitProfiledDbProviderFactory(IDbProfiler profiler, DbProviderFactory tail)
        {
            base.InitProfiledDbProviderFactory(profiler, tail);
            _tail = tail;
        }
    
        public override DbDataAdapter CreateDataAdapter()
        {
            return new DbDataAdapterForEntLib(base.CreateDataAdapter());
        }
    
        public override DbCommand CreateCommand()
        {
            return _tail.CreateCommand(); 
        }        
    }
    
    public class DbDataAdapterForEntLib : DbDataAdapter
    {
        private DbDataAdapter _dbDataAdapter;
        public DbDataAdapterForEntLib(DbDataAdapter adapter)
        : base(adapter)
       {
            _dbDataAdapter = adapter;
       }
    }
    
  2. В Web.config добавьте ProfiledDbProviderFactoryForEntLib в DbProviderFactories и установите ProfiledDbProviderFactoryForEntLib в качестве providerName для вашей строки подключения

    <configuration>
        <configSections>
            <section name="dataConfiguration" type="..."  />
        </configSections>
        <connectionStrings>
            <add name="SqlServerConnectionString" connectionString="Data Source=xyz;Initial Catalog=dbname;User ID=u;Password=p"
      providerName="ProfiledDbProviderFactoryForEntLib" />
        </connectionStrings>
        <system.data>
            <DbProviderFactories>
              <add name="EntLib DB Provider"
               invariant="ProfiledDbProviderFactoryForEntLib"
               description="Profiled DB provider for EntLib"
               type="MvcApplicationEntlib.ProfiledDbProviderFactoryForEntLib,     MvcApplicationEntlib, Version=1.0.0.0, Culture=neutral"/>
            </DbProviderFactories>
        </system.data>
        <dataConfiguration defaultDatabase="..." />
        <appSettings>... <system.web>... etc ...
    </configuration>
    

    (MvcApplicationEntlib - это название моего тестового проекта)

  3. Настройте ProfiledDbProviderFactoryForEntLib перед любыми вызовами в БД (читатели, чувствительные к взлому, должны быть предупреждены, это то, где это становится уродливым)

    //In Global.asax.cs 
        protected void Application_Start()
        {
    
            ProfiledDbProviderFactoryForEntLib profiledProfiledDbProviderFactoryFor = ((ProfiledDbProviderFactoryForEntLib)DbProviderFactories.GetFactory("ProfiledDbProviderFactoryForEntLib"));
            DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); //or whatever predefined factory you want to profile
            profiledProfiledDbProviderFactoryFor.InitProfiledDbProviderFactory(MiniProfiler.Current, factory); 
        ...
    

    Вероятно, это можно было бы сделать лучше или в другом месте. MiniProfiler.Current здесь будет нулевым, потому что здесь ничего не профилируется.

  4. Вызовите хранимую процедуру так же, как вы делали с самого начала

    public class HomeController : Controller
        {
            public ActionResult Index()
            { 
                Database db = DatabaseFactory.CreateDatabase();
                DbCommand dbCommand = db.GetStoredProcCommand("spGetSomething");
                DbCommand cmd = new ProfiledDbCommand(dbCommand, dbCommand.Connection, MiniProfiler.Current);
                DataSet ds = db.ExecuteDataSet(cmd);
                ...
    

Edit: Хорошо, не был уверен, как именно ты хотел это использовать. Пропустить ручное создание ProfiledDbCommand. ProfiledDbProviderFactory необходимо запускать с минипрофилем для каждого запроса.

  1. В файле Global.asax.cs удалите изменения, внесенные в Application_Start (заводская настройка на шаге 3 выше), вместо этого добавьте это в Application_BeginRequest.

    ProfiledDbProviderFactoryForEntLib profiledProfiledDbProviderFactoryFor = ((ProfiledDbProviderFactoryForEntLib) DbProviderFactories.GetFactory("ProfiledDbProviderFactoryForEntLib"));
    DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
    profiledProfiledDbProviderFactoryFor.InitProfiledDbProviderFactory(MvcMiniProfiler.MiniProfiler.Start(), factory);
    
  2. Удалите метод CreateCommand из ProfiledDbProviderFactoryForEntLib, чтобы позволить ProfiledDbProviderFactory создать вместо этого профилированную команду.

  3. Выполните ваш SP без создания команды ProfiledDbCommand, как это

    Database db = DatabaseFactory.CreateDatabase();
    DbCommand dbCommand = db.GetStoredProcCommand("spGetSomething");
    DataSet ds = db.ExecuteDataSet(dbCommand);
    
0 голосов
/ 09 августа 2013

Это довольно старый пост, но я думаю, стоит упомянуть, как я с этим разобрался.

Из-за того, как наше приложение было реализовано, применить решение от Jonas было нелегко, поэтому я пошел по пути изменения блока данных EntLib (который я уже модифицировал в нашем проекте).

Это было так же просто, как просто изменить методы DoExecuteXXX в классе Database, чтобы они вызывали минипрофильтр, который я ранее изменил, чтобы выставить SqlProfiler как public.

Просто делаю:

if (_miniProfiler != null)
    _miniProfiler.ExecuteStart(command, StackExchange.Profiling.Data.ExecuteType.NonQuery);

в начале каждого метода и

if (_miniProfiler != null)
    _miniProfiler.ExecuteFinish(command, StackExchange.Profiling.Data.ExecuteType.NonQuery); 

в блоке finally сделал свое дело. Просто измените тип выполнения на соответствующий изменяемому методу.

Экземпляр минипрофиля был получен в конструкторе DatabaseClass

if (MiniProfiler.Current != null)
    _miniProfiler = MiniProfiler.Current.SqlProfiler;
0 голосов
/ 17 августа 2011

Вы можете попробовать что-то вроде этого.

Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand(PROC);

ProfiledDbCommand cmd = new ProfiledDbCommand(dbCommand, dbCommand.Connection, MvcMiniProfiler.MiniProfiler.Current);
db.AddInParameter(cmd, "foo", DbType.Int64, 0);

DbConnection dbc = MvcMiniProfiler.Data.ProfiledDbConnection.Get(dbCommand.Connection, MiniProfiler.Current);
DataSet ds = db.ExecuteDataSet(dbc);

Обновление:

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

Похоже, это проблема Mvc-Mini-Profiler.Если вы посмотрите на эту Issues 19 Link на домашней странице проекта MVC-Mini-Profiler, вы увидите, что другие люди сталкиваются с такой же проблемой.

Я потратил довольно много времени на то, чтобы System.Data.SqlClient с Reflector и ProfiledDbProviderFactory пытался выяснить, в чем проблема, и, похоже, в следующем заключается проблема.Это можно найти в классе ProfiledDbProviderFactory.

 public override DbDataAdapter CreateDataAdapter()        
 {
     return tail.CreateDataAdapter();        
 }

Теперь проблема не совсем в этом коде, а в том, что когда класс Microsoft.Practices.EnterpriseLibrary.Data.Database вызывает экземпляр DbDataAdapter (tail в нашем случае - SqlClientFactory)он создает SqlClientFactory Command, а затем пытается установить для Connection этой команды значение ProfiledDbConnection, и это будет его поломкой.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...