如何在内存中进行事务处理?

Nei*_*ell 4 c# transactions

我对使用事务RDBMS非常熟悉,但是如果事务失败,我如何确保对内存数据所做的更改被回滚?如果我甚至不使用数据库怎么办?

这是一个人为的例子:

public void TransactionalMethod()
{
    var items = GetListOfItems();

    foreach (var item in items)
    {       
        MethodThatMayThrowException(item);

        item.Processed = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的示例中,我可能希望以某种方式回滚对列表中项目所做的更改,但是如何实现此目的呢?

我知道"软件事务存储器",但对它不太了解,看起来相当实验性.我也知道"可补偿交易"的概念,但这会产生编写执行/撤消代码的开销.

Subversion似乎通过让你运行"cleanup"命令来处理更新工作副本的错误.

有任何想法吗?

更新:
Reed Copsey提供了一个很好的答案,包括:

处理数据副本,在提交时更新原始数据.

这使我的问题更进一步 - 如果在提交期间发生错误怎么办?我们经常将提交视为即时操作,但实际上它可能会对大量数据进行许多更改.如果OutOfMemoryException在应用提交时存在不可避免的事情,会发生什么?

另一方面,如果有人选择回滚选项,如果在回滚期间出现异常会发生什么?我理解像Oracle RDBMS这样的东西有回滚段和UNDO日志和东西的概念,但假设没有序列化到磁盘(如果没有序列化到磁盘,它没有发生,崩溃意味着你可以调查那些日志并从中恢复),这真的可能吗?

更新2:
一个答案亚历克斯做了一个很好的建议:即一个更新不同的对象,然后,提交阶段只不过是修改过的新对象的引用到当前对象.他进一步建议你改变的对象实际上是修改对象的列表.

我明白他在说什么(我想),我想让问题更复杂:

在这种情况下,你如何处理锁定?想象一下,你有一个客户列表:

var customers = new Dictionary<CustomerKey, Customer>();
Run Code Online (Sandbox Code Playgroud)

现在,您想要对其中一些客户进行更改,如何在不锁定和替换整个列表的情况下应用这些更改?例如:

var customerTx = new Dictionary<CustomerKey, Customer>();

foreach (var customer in customers.Values)  
{
    var updatedCust = customer.Clone();     
    customerTx.Add(GetKey(updatedCust), updatedCust);

    if (CalculateRevenueMightThrowException(customer) >= 10000)
    {
        updatedCust.Preferred = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

我该怎么做?这(Alex的建议)将意味着在替换列表引用时锁定所有客户:

lock (customers)
{
    customers = customerTx;
}
Run Code Online (Sandbox Code Playgroud)

然而,如果我循环,修改原始列表中的引用,它不是原子的,并且与"如果它在中途崩溃的问题"中的问题相违背:

foreach (var kvp in customerTx)
{
    customers[kvp.Key] = kvp.Value;
}
Run Code Online (Sandbox Code Playgroud)

Ree*_*sey 8

几乎所有这样做的选择都需要以下三种基本方法之一:

  1. 在修改之前复制数据,如果中止则恢复为回滚状态.
  2. 处理数据副本,在提交时更新原始数据.
  3. 记录您的数据更改,以便在中止的情况下撤消它们.

例如,您提到的软件事务内存遵循第三种方法.关于这一点的好处是它可以乐观地处理数据,并且只是在成功提交时丢弃日志.