10

我确信可以分析企业库 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”类型。

4

3 回答 3

13

异常来自 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 设置为您的连接字符串的 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);
                ...
    

编辑:好的,不确定你想如何使用它。跳过手动创建 ProfiledDbCommand。ProfiledDbProviderFactory 需要使用 miniprofiler 为每个请求启动。

  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 改为创建分析命令。

  3. 在不创建 ProfiledDbCommand 的情况下执行您的 SP,如下所示

    Database db = DatabaseFactory.CreateDatabase();
    DbCommand dbCommand = db.GetStoredProcCommand("spGetSomething");
    DataSet ds = db.ExecuteDataSet(dbCommand);
    
于 2011-08-26T21:43:51.310 回答
0

这是相当古老的帖子,但我认为值得一提的是我是如何整理出来的。

由于我们的应用程序的实现方式,应用 Jonas 的解决方案并不容易,所以我走上了更改 EntLib 数据块的道路(我已经在我们的项目中修改了它)。

事情很简单,只需修改 Database 类中的方法 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 块中成功了。只需将执行类型更改为适合正在更改的方法即可。

在 DatabaseClass 的构造函数中获取 miniprofiler 实例

if (MiniProfiler.Current != null)
    _miniProfiler = MiniProfiler.Current.SqlProfiler;
于 2013-08-09T09:17:08.767 回答
-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 Data 源代码 SqlDatabase 进行了大量搜索之后,看起来并没有一个简单的方法来实现这一点。对不起。我知道我是。

这实际上似乎是 Mvc-Mini-Profiler 的一个问题。如果您在 MVC-Mini-Profiler 项目主页上查看此问题 19 链接,您会看到其他人也有同样的问题。

我已经花了很长时间通过 System.Data.SqlClient 与 Reflector 和 ProfiledDbProviderFactory,试图查看问题是什么,似乎以下是问题所在。这可以在 ProfiledDbProviderFactory 类中找到。

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

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

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

于 2011-08-17T14:11:44.250 回答