MVC迷你分析器与微软企业库

本文关键字:微软 企业库 分析器 MVC | 更新日期: 2023-09-27 18:05:57

我确信可以对企业库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'.

MVC迷你分析器与微软企业库

异常来自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。将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. 在调用DB之前设置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);
    

这是一篇相当老的文章,但我认为值得一提的是我是如何整理的。

由于我们的应用程序的实现方式,应用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块中的

完成了任务。只需将Execution类型更改为与所更改的方法相适应的类型。

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

if (MiniProfiler.Current != null)
    _miniProfiler = MiniProfiler.Current.SqlProfiler;

你可以试试这样做。

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,然后试图设置这个命令的ConnectionProfiledDbConnection,这是它打破。

我试过创建自己的工厂类来解决这个问题,但我只是迷路了。也许当我有空闲时间的时候,我会尝试解决这个问题,除非SO团队比我抢先一步。:)