SQL 执行非查询时异常:是否可以避免回滚

本文关键字:是否 可以避免 异常 执行 查询 SQL | 更新日期: 2023-09-27 18:30:46

流被写入var-binary字段,如下所示:

string connectionString = "connection String";
using (SqlConnection conn = new SqlConnection(connectionString)) {
    conn.Open();
    using (SqlCommand cmd = new SqlCommand("update TableX set Data=@data where id = @id")) {
        cmd.Parameters.AddWithValue("@Id", ParameterDirection.Input);
        cmd.Parameters.AddWithValue("@Data", streamToStore);
        cmd.Connection = conn;
        try {
            int rows = cmd.ExecuteNonQuery();
        } catch (Exception ex) {
            // store the so far written data anyway!
        }
    }
}

发生异常时,到目前为止写入的所有数据都将消失。因此,在发生异常的情况下似乎存在某种回滚。无论如何,有没有办法存储到目前为止写入的数据?稍后会恢复,所以我不在乎它是否暂时不完整。

快速示例:我正在使用的流是网络流。数据在写入数据库时被提取。假设流的大小为 1000 字节。如果已经接收到900字节(并以某种方式自动缓冲在数据库中的某个位置),然后发生连接问题(IOException),则所有数据都将丢失。不会将任何内容存储到数据库中。但是我想将这 900 个字节存储到 var-binary 字段中(稍后会恢复其余部分)。

SQL 执行非查询时异常:是否可以避免回滚

几件事:

  • 我相当确定 SqlServer 不支持"部分列更新"的概念。更新要么完全发生,要么不会发生。

  • 您可以尽可能多地将流复制到字节数组或 MemoryStream 中,然后将其传递,而不是直接将流作为参数传递。这将使您能够控制错误处理。这不起作用的唯一原因是 (1) .NET sql 客户端能够将字节流式传输到数据库,而无需先将它们全部收集到内存中,以及 (2) 依赖于此优化。

  • 另一种可能的解决方法是分批进行写入。不要尝试一次写入整个流,而是创建一个循环,在其中读取流的下一个 N 个字节,并发出一个查询,尝试将这些字节追加到现有列。这将导致更多的数据库往返,但应该会获得所需的部分更新。

  • 我假设有问题的"错误"发生在对 Stream.Read() 的调用中,这会中断将数据从流中提取到 SQL 的过程。如果这是真的,那么另一个可能的解决方案可能是将您的流包装在另一个 Stream 实现中,该实现捕获相关错误并在那里结束流。像这样:


class ErrorSuppressingStream : Stream
{
    private readonly Stream inner;
    public bool TerminatedWithError { get; private set; }
    public ErrorSuppressingStream(Stream inner) { this.inner = inner; }
    public override int Read(byte[] buffer, int offset, int count)
    {
        if (this.TerminatedWithError) { return 0; }
        try { return this.inner.Read(buffer, offset, count); }
        catch (XXXException ex)
        {
            this.TerminatedWithError = true;
            return 0;
        }
    }
    // more overrides here
}

这将允许您仍将字节流式传输到数据库,但会将读取错误视为流结束而不是抛出。