我们的代码使用以下代码向数据库发送查询报告:
public DataTable RunQuery(QueryDatabase queryDatabase, string theQuery, IEnumerable<DatabaseParameter> parameters, IEnumerable<DbParameterList> listParameters)
{
try
{
using (SqlConnection connection = new SqlConnection(ConnectionString(queryDatabase)))
{
connection.Open();
using (System.Data.SqlClient.SqlCommand cmdToExecute = new System.Data.SqlClient.SqlCommand())
{
cmdToExecute.CommandText = theQuery;
cmdToExecute.CommandType = CommandType.Text;
cmdToExecute.CommandTimeout = 900;
foreach (DatabaseParameter p in parameters)
{
cmdToExecute.Parameters.Add(p.AsSqlParameter());
}
foreach (DbParameterList l in listParameters)
{
l.AddToCommand(cmdToExecute);
}
using (System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter(cmdToExecute))
{
cmdToExecute.Connection = connection;
DataTable dt = new DataTable();
int _numberOfRowsAffected = adapter.Fill(dt);
return (dt);
}
}
}
}
catch (Exception e)
{
Log.LogError("DatabaseService.RunQuery", e.Message);
return null;
}
}
这对我们来说效果很好。实际上,未显示的是,通过对同一方法的另一次调用(Transactional与Reporting数据库),可以很好地从数据库中提取连接字符串。
最近,出于业务需求,我们遇到了SQL Server的一个限制:表中只能有大约300列。一份报告在临时表中生成约700列。这样做不行,所以我决定将结果拆分成几个查询并将结果表拼接在代码中(谢谢微软,我不应该这样做)。
代码如下所示:
DataTable result = DatabaseService.Instance.RunQuery(QueryDatabase, Query, Parameters, ListParameters);
resetSlicer();
IList<T> paramterSlice = getParameterSlice();
while (paramterSlice.Count > 0)
{
DataTable slice = getSlice(paramterSlice);
result.AppendTableColumns(slice, KeyColumns);
paramterSlice = getParameterSlice();
}
getSlice()调用包含另一个“RunQuery()”调用。
当我运行此代码时,首先调用“GetSlice()”失败,但出现以下异常:
A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.)
堆栈跟踪如下:
at System.Data.SqlClient.TdsParser.TdsExecuteRPC(_SqlRPC[] rpcArray, Int32 timeout, Boolean inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, Boolean isCommandProc, Boolean sync, TaskCompletionSource`1 completion, Int32 startRpc, Int32 startParam)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable[] dataTables, Int32 startRecord, Int32 maxRecords, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable dataTable)
at <Anonymized>.Common.Database.DatabaseService.RunQuery(QueryDatabase queryDatabase, String theQuery, IEnumerable`1 parameters, IEnumerable`1 listParameters)
为了尝试诊断问题,我注释掉了第一个“RunQuery()”调用。这导致第一个“getSlice()”正确返回结果;但是,第二次调用失败并出现同样的错误。
鉴于我们已经能够在代码中的其他地方使用此模式运行多个查询,为什么这个特定用法会产生错误?
答案 0 :(得分:0)
帽子提示Mark Peters指向正确的方向。
存在DbParameterList以将列表传递给SQL(例如,尝试在单个语句中查找姓氏为#34; Smith&#34;,&#34; Doe&#34;或&#34; Jones&#34; )。这避免了在SQL中使用插值IN(...)列表的丑陋构造。为此,该类实现IEnumerable接口。我假设微软会正确使用自己的接口。在这种情况下,我假设在枚举IEnumerable之前,他们会调用IEnumerable上的Reset()方法。但是,他们没有。
第一次调用DbParameterList时,枚举器处于开头且一切正常。第二次调用会使连接崩溃(是的,如果在SqlParameter中使用的IEnumerable为空,则连接崩溃),因为枚举器从未被重置。
因此,在枚举器附加到SqlCommand时添加一个Reset()调用可以解决问题。