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
}
这将允许您仍然将字节流式传输到数据库,但会将读取错误视为流结束而不是抛出。
流被写入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
}
这将允许您仍然将字节流式传输到数据库,但会将读取错误视为流结束而不是抛出。