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 字段中(稍后会恢复其余部分)。
几件事:
-
我相当确定 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
}
这将允许您仍将字节流式传输到数据库,但会将读取错误视为流结束而不是抛出。