带有Microsoft Enterprise Library的MVC Mini Profiler

时间:2011-08-17 13:48:24

标签: c# enterprise-library mvc-mini-profiler

我确信可以分析企业库SQL命令,但我无法弄清楚如何包装连接。这就是我想出的:

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 个答案:

答案 0 :(得分:11)

异常来自Entlib Database.DoLoadDataSet

中的这一行
    ((IDbDataAdapter) adapter).SelectCommand = command; 

在这种情况下,适配器的类型为SqlDataAdapter,它需要一个SqlCommand,ProfiledDbProviderFactory创建的命令的类型为ProfiledDbCommand,如异常所示。

此解决方案将通过覆盖ProfiledDbProviderFactory中的CreateDataAdapter和CreateCommand为EntLib提供通用的DbDataAdapter。 它似乎可行,但如果我监督了这个黑客可能造成的任何不必要的后果(或者它可能造成的眼睛疼痛),我道歉。 在这里:

  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设置为您的connectionstring的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在这里将为null,因为此处没有任何内容。

  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);
                ...
    
  5. 编辑: 好吧不确定你想如何使用它。跳过手动创建ProfiledDbCommand。需要使用miniprofiler为每个请求启动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. 从ProfiledDbProviderFactoryForEntLib中删除方法CreateCommand,让ProfiledDbProviderFactory改为创建profiled命令。

    3. 执行您的SP而不创建ProfiledDbCommand,就像这样

      Database db = DatabaseFactory.CreateDatabase();
      DbCommand dbCommand = db.GetStoredProcCommand("spGetSomething");
      DataSet ds = db.ExecuteDataSet(dbCommand);
      

答案 1 :(得分:0)

这是一个相当古老的帖子,但我认为值得一提的是我如何排除这一点。

由于我们的应用程序的实现方式,Jonas应用解决方案并不容易,所以我开始更改EntLib Datablock(我已在项目中修改过)。

事情就像修改数据库类中的方法DoExecuteXXX一样简单,因此他们调用了miniprofiler,之前我已将其更改为将SqlProfiler公开为公共。

正好:

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

在每种方法的开头和

if (_miniProfiler != null)
    _miniProfiler.ExecuteFinish(command, StackExchange.Profiling.Data.ExecuteType.NonQuery); 
在finally块中的

做了伎俩。只需将执行类型更改为适合要更改的方法。

miniprofiler实例是在DatabaseClass

的构造函数中获得的
if (MiniProfiler.Current != null)
    _miniProfiler = MiniProfiler.Current.SqlProfiler;

答案 2 :(得分:-2)

你可以尝试这样的事情。

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的一个问题。如果您在MVC-Mini-Profiler项目主页上查看此Issues 19 Link,您将看到其他人遇到相同的问题。

我花了很长一段时间通过带有Reflector的System.Data.SqlClient和ProfiledDbProviderFactory,试图看看问题是什么,看起来好像以下是问题所在。这可以在ProfiledDbProviderFactory类中找到。

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

现在问题不在于这个代码,而是当Microsoft.Practices.EnterpriseLibrary.Data.Database类调用DbDataAdapter的intance(在我们的例子中是尾部是SqlClientFactory)时,它会创建SqlClientFactory Command,并且然后尝试将此命令的Connection设置为ProfiledDbConnection,这就是它中断了。

我已经尝试创建自己的工厂类来解决这个问题但是我太迷茫了。也许当我有空闲时间时,我会尝试解决这个问题,除非SO团队打败我。 :)