【问题标题】:Exception while SQL ExecuteNonQuery: Is it possible to avoid a rollback?SQL ExecuteNonQuery 时出现异常:是否可以避免回滚?
【发布时间】:2016-01-18 14:01:36
【问题描述】:

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

【问题讨论】:

  • 您必须在关闭连接之前调用 Commit ...否则您的更改将被隐式回滚...
  • 确实如此,稍后会完成提交。但这不是问题。问题是,流可以表示写入数据库的巨大 blob。如果在该操作期间发生异常,则数据库中根本没有数据,尽管它已经运行了很长时间。
  • 稍后但仍在 using (SqlConnection ... block?
  • @PrfctByDsgn SqlConneciton 没有Commit
  • 单个更新语句总是完全执行或根本不执行...

标签: c# sql-server varbinary


【解决方案1】:

一些事情:

  • 我相当肯定 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
}

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

【讨论】:

  • 谢谢!!您是对的,将数据存储在内存中不是一种选择,我们依靠 .net 将数据直接流式传输到数据库,因此我们不会获得巨大的内存峰值。但是,您批量写作的想法听起来很有趣。我想我会试一试!
猜你喜欢
  • 2016-07-14
  • 1970-01-01
  • 2018-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-15
  • 2015-03-02
相关资源
最近更新 更多