【问题标题】:Why does a second query generate "A transport-level error has occurred when sending the request to the server" message?为什么第二个查询会生成“向服务器发送请求时发生传输级错误”消息?
【发布时间】:2014-10-09 08:56:51
【问题描述】:

我们的代码使用以下代码向数据库发送查询以获取我们的报告:

    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;
        }
    }

这对我们来说效果很好。事实上,没有显示的是连接字符串很可能通过对同一方法的另一个调用(事务数据库与报告数据库)从数据库中提取出来。

最近,出于业务需求,我们遇到了 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()”正确返回结果;但是,第二次调用失败并出现同样的错误。

鉴于我们已经能够在代码的其他地方使用这种模式运行多个查询,为什么这种特殊用法会产生错误?

【问题讨论】:

  • "一个表中只能有 ~300 列。" - 如果一个表中有大约 300 列,那么它很有可能是错误的
  • 请注意,我说的是临时表(实际上是优化器生成的)。由于业务需求,查询的输出可能是那么多列,我无能为力。
  • 我注意到您正在将 SqlCommand 添加到每个 DBParameterList 中,可能是出于某种日志记录/跟踪原因?但是,这些 SqlCommand 将在您可以使用它们时被处理掉。此外,他们可能正在处理附加的 SqlParameters。您可以尝试重用 Disposed 参数吗?
  • DBParameterList 使用该惯用语是因为我在了解扩展方法之前就编写了它,而扩展方法实际上就是该方法。 DbParamterList 封装了一个 SqlParameter 的信息,它有一个值列表作为它的“值”。我将审查该代码是否存在可能的重用问题。

标签: c# sql-server


【解决方案1】:

向马克彼得斯致敬,因为他指出了正确的方向。

存在 DbParameterList 以将列表传递给 SQL(例如,尝试在单个语句中查找姓氏为“Smith”、“Doe”或“Jones”的人)。这避免了 SQL 中带有插入 IN (...) 列表的丑陋构造。为此,该类实现了 IEnumerable 接口。我假设微软会正确使用他们自己的接口。在这种情况下,我假设在枚举 IEnumerable 之前,他们会调用 IEnumerable 上的 Reset() 方法。但是,他们没有。

第一次调用 DbParameterList 时,枚举器是在开始时,一切正常。第二次调用将使连接崩溃(是的,如果 SqlParameter 中使用的 IEnumerable 为空,则连接崩溃),因为从未重置枚举器。

因此,在枚举数附加到 SqlCommand 时向枚举数添加 Reset() 调用可以解决问题。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-25
  • 1970-01-01
  • 2015-12-07
相关资源
最近更新 更多