从SqlDataReader

时间:2017-03-14 23:30:32

标签: c# json sqldatareader

我试图从格式化为JSON的SQL Server 2016数据库返回一个相当大的数据集(几千行)(使用SQL Server 2016的FOR JSON AUTO功能);但是,我遇到了从SqlDataReader读取结果的问题。

我之前将结果作为常规行加载到DataTable中,这非常有效(加载整个表大约需要10-15秒)。但是,如果我尝试使用相同的数据构建返回为JSON的字符串,则需要几分钟时间来构建从DataReader返回的字符串。我想补充一点,我也在这个查询中返回了大量的二进制数据(我有试图从数据库中检索的SqlGeometry对象)。我从这里返回的典型字符串是几十个字符长,读者的读数非常慢。

无论如何,我用于解析JSON的加载代码如下:

public static Task<string> ExecuteJsonReaderAsync(string connectionString, CommandType commandType, string commandText, SqlParameter[] oParams = null, int timeout = 30, CancellationToken token = default(CancellationToken))
    {
        return Task<string>.Factory.StartNew(() =>
        {
            var str = string.Empty;
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                using (var command = new SqlCommand(commandText, connection))
                {
                    command.CommandTimeout = timeout;
                    command.CommandType = commandType;

                    if (oParams?.Length > 0) command.Parameters.AddRange(oParams);

                    using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
                    {
                        while (reader.Read())
                        {
                            str = $"{str}{reader[0]}";
                        }
                        reader.Close();
                    }    

                    return str;
                }
            }
        }, token);    
    }

我尝试过各种命令选项来尝试加速它,包括CloseConnection,SequentialAccess,SingleResult,但无济于事。为什么构建字符串比从相同数据加载DataTable要花费更长的时间,有没有更快的方法来实现这一点?

我认为这必须是我做错了或我忽略的事情,我希望有人之前遇到过这个问题。有什么想法吗?

2 个答案:

答案 0 :(得分:2)

您的代码在每个循环中重新分配内存中的字符串变量。这不利于代码的性能。相反,类StringBuilder有一个内部缓冲区,允许更少的内存重新分配,你也可以控制这个缓冲区的大小,以避免重新分配,如果你知道数据的总长度。

所以

// Set an initial capacity of 1MB
StringBuilder str = new StringBuidler(1024*1024);
while (reader.Read())
{
    str.Append(reader[0].ToString());
}

....
return str.ToString();

有关C# string immutable concept here

的更多信息

答案 1 :(得分:0)

这里有几个问题:

  1. 您通过 $"{str}{reader[0]}" 使用字符串格式对 GC 的性能影响很大。您应该使用 StringBuilderAppend() 方法。
  2. 你真的应该像这样一直使用async

以下是我将如何编写代码以获得更好的性能(通过异步缩放和通过 StringBuilder 进行内存缩放):

        public static async Task<string> ExecuteJsonReaderAsync(string connectionString, CommandType commandType, string commandText, SqlParameter[] oParams = null, int timeout = 30, CancellationToken token = default(CancellationToken))
        {
            var str = string.Empty;
            await using var connection = new SqlConnection(connectionString);

            await connection.OpenAsync(token);

            await using var command = new SqlCommand(commandText, connection);
            command.CommandTimeout = timeout;
            command.CommandType = commandType;

            if (oParams?.Length > 0) command.Parameters.AddRange(oParams);

            var stringBuilder = new StringBuilder(1024 * 1024);
            await using var reader = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection, token);
            while (await reader.ReadAsync(token))
            {
                stringBuilder.Append(reader[0]);
            }
            await reader.CloseAsync();

            return stringBuilder.ToString();
        }