SQL ExecuteNonQuery 时出现异常:是否可以避免回滚?

Exception while SQL ExecuteNonQuery: Is it possible to avoid a rollback?

流被写入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 字段中(稍后将恢复其余部分)。

几件事:

  • 我很确定 SqlServer 不支持 "partial column updates" 的概念。更新要么完全发生,要么不会。

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

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

  • 我假设有问题的 "error" 发生在对 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
}

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