3

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

系统的多个用户可以将子实体添加到特定根。例如,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 个用户大致同时提交他们的提案怎么办?UI 发现尚未达到限制,并显示网页以向两个用户提交提案。当第一个用户提交时,一切都很好。现在,只要第一个用户在第二个用户之前提交,第二个用户就可以了,这样当第二个用户提交时,从数据库中检索数据并且限制是准确的。

这是一个有争议的问题吗?对于 2 个用户几乎同时提交的罕见情况,我是否应该依赖数据库中的约束(ProposalLimit >= ProposalCount)?

4

1 回答 1

2

如果您将业务规则检查(即限制检查)放入事务中,那就没问题了。那是

  1. 触发添加提案命令的用户点击按钮
  2. 代码开始一个新事务。请参阅此处了解我建议您如何使用交易
  3. 从数据库加载 RequestForProposal 对象,或刷新它。我建议你使用升级锁。
  4. 将新提案添加到根。检查限制约束,抛出异常它失败。
  5. 提交交易

这样做是在使用数据库并发控制。我认为没有其他方法可以做到这一点。

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

如果您在步骤 3 中使用升级锁,这将避免死锁。基本上,当第二个用户为相同的聚合根提交提案时,数据库不会让您在第一个事务提交之前读取它。

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

于 2010-11-05T09:44:27.610 回答