异常来自Entlib Database.DoLoadDataSet中的这一行。
((IDbDataAdapter) adapter).SelectCommand = command;
在这种情况下,适配器的类型为SqlDataAdapter,它期望一个SqlCommand。由ProfiledDbProviderFactory创建的命令的类型是ProfiledDbCommand,正如你在异常中看到的。
此解决方案通过覆盖ProfiledDbProviderFactory中的CreateDataAdapter和CreateCommand方法,为EntLib提供了通用的DbDataAdapter。它似乎工作得很好,但如果我忽略了这个 hack 可能带来的任何不良后果(或可能引起的不适),我深表歉意。下面是具体步骤:
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;
}
}
在 Web.config 中,向 DbProviderFactories 添加 ProfiledDbProviderFactoryForEntLib 并将 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是我的测试项目的名称)
在任何对数据库的调用之前设置ProfiledDbProviderFactoryForEntLib(对于敏感于黑客攻击的读者,请注意,这是一个丑陋的地方)
protected void Application_Start()
{
ProfiledDbProviderFactoryForEntLib profiledProfiledDbProviderFactoryFor = ((ProfiledDbProviderFactoryForEntLib)DbProviderFactories.GetFactory("ProfiledDbProviderFactoryForEntLib"));
DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
profiledProfiledDbProviderFactoryFor.InitProfiledDbProviderFactory(MiniProfiler.Current, factory);
...
这样做可能有更好的方式或在其他地方进行操作。这里MiniProfiler.Current将为空,因为没有对其进行性能分析。
像一开始那样调用存储过程即可。
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。
在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)
删除 ProfiledDbProviderFactoryForEntLib 中的 CreateCommand 方法,让 ProfiledDbProviderFactory 创建 profiled 命令。
执行您的存储过程时不要创建 ProfiledDbCommand,就像这样:
Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand("spGetSomething");
DataSet ds = db.ExecuteDataSet(dbCommand);