如何通过NHibernate处理聚合根中的持久计算属性的并发?

时间:2010-10-14 20:44:40

标签: c# nhibernate concurrency domain-driven-design aggregateroot

我需要使用聚合根持久保存计算属性。计算基于子实体。我使用root来通过域方法添加/删除子项,这些方法更新了calculate属性。

可以由系统的多个用户将子实体添加到特定根。例如,UserA可以将子项添加到Root123,UserB也可以将子项添加到Root123。

如果多个用户可能将子实体添加到不同事务中的同一根,我如何确保准确保留此计算属性?在我的特定情况下,计算的属性用于确保不超过某个限制,由根上的另一个属性设置。


以下是问题的一个更具体的例子:

public class RequestForProposal : AggregateRoot {
    ...
    private ISet<Proposal> _proposals = new HashedSet<Proposal>();

    public virtual int ProposalLimit { get; set; }
    public virtual int ProposalCount { get; protected set; }

    public virtual IEnumerable<Proposal> Proposals {
        get { return _proposals; }
    }
    ...

    public virtual void AddProposal(User user, Content proposalContent) {
        if (ProposalCount >= ProposalLimit) {
            throw new ProposalLimitException("No more proposals are being accepted.");
        }

        var proposal = new Proposal(user, proposalContent);
        _proposals.Add(proposal);
        ProposalCount++;
    }

    public virtual void RemoveProposal(Proposal proposalToRemove) {
        _proposals.Remove(proposalToRemove);
        ProposalCount--;
    }
}

如果2个用户几乎同时提交提案,该怎么办?用户界面看到尚未达到限制,并显示网页以向两个用户提交提案。当第一个用户提交时,一切都很顺利。现在,只要第一个用户在第二个用户之前提交,第二个用户就可以了,这样当第二个用户提交时,数据将从数据库中检索出来,并且限制将是准确的。

这是一个有争议的问题吗?我是否应该依赖DB中的约束(ProposalLimit&gt; = ProposalCount)来处理2个用户几乎同时提交的罕见情况?

1 个答案:

答案 0 :(得分:2)

如果您要在交易中进行业务规则检查(即限额检查),那就没问题了。那是

  1. 触发添加提案命令的用户点击按钮
  2. 代码启动新事务。请参阅here for how I suggest you use transactions
  3. 从db加载RequestForProposal对象,或者刷新它。我建议你使用升级锁。
  4. 将新提案添加到根目录。检查限制约束,抛出失败的异常。
  5. 提交交易
  6. 这样做就是使用数据库concurrency controls。我认为没有其他办法可以做到这一点。

    这会产生一些争用,但您可以采取一些措施来尽量减少这种争用。即在步骤3中确保您选择的db列具有索引。这将导致行锁,而不是页锁。

    如果在步骤3中使用升级锁定,则可以避免死锁。基本上,当第二个用户提交相同聚合根的提议时,db将不允许您在第一个事务提交之前读取它。

    您还应该考虑在Proposal.RequestForProposalId上添加db索引,它将有助于提升性能,因为这是Proposal加载的列。如果它有助于最小化该表上任何锁的范围,我不是100%,但它可能......