如何通过 NHibernate 处理聚合根中持久计算属性的并发?
我需要保留具有聚合根的计算属性。计算基于子实体。我使用根通过域方法添加/删除子项,这些方法更新计算属性。
系统的多个用户可以将子实体添加到特定的根。例如,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)?
I have the need to persist a calculated property with an aggregate root. The calculation is based on child entities. I am using the root to add/remove the children via domain methods, and these methods update the calculate property.
A child entity can be added to a particular root by multiple users of the system. For example, UserA can add a child to Root123, and UserB can also add a child to Root123.
How can I ensure that this calculated property is persisted accurately when more than one user may be adding child entities to the same root in different transactions? In my particular case, the calculated property is used to ensure that some limit is not exceeded, as set by another property on the root.
Here is a more concrete example of the problem:
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--;
}
}
What if 2 users are submitting their proposals at roughly the same time? The UI sees that the limit has not yet been reached and shows the Web page to submit a proposal for both users. When the first user submits, all is well. Now, the second user will be okay as long as the first user submits before the second one, so that when the second user submits, the data is retrieved from the DB and the limit will be accurate.
Is this a moot point? Should I rely on constraints in the DB (ProposalLimit >= ProposalCount) for those rare cases where 2 users submit nearly simultaneously?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您将业务规则检查(即限制检查)放在交易中,那就没问题了。即
通过这种方式,您可以使用数据库并发控制。我认为没有其他方法可以做到这一点。
这会产生一些争用,但您可以采取一些步骤来最大程度地减少争用。即确保在步骤 3 中您选择的数据库列有索引。这将导致行锁,而不是页锁。
如果您在步骤 3 中使用升级锁,这将避免死锁。基本上,当第二个用户提交同一聚合根的提案时,数据库将不会让您读取它,直到第一个事务提交为止。
您还应该考虑在 Proposal.RequestForProposalId 上添加数据库索引,这将有助于提高性能,因为这是加载提案的列。我不是 100% 认为它有助于最小化该表上任何锁的范围,但它可能......
If you were to put the Business Rule Check (ie limit check) inside a transaction, it will be fine. That is
Doing it this way you are using the databases concurrency controls. I don't think there is any other way of doing it.
This will create some contention, but you can take a few steps to minimize that. Namely make sure in Step 3 that the db column you select by has an index on it. This will cause a row lock, instead of a page lock.
If you use an Upgrade lock in step 3, this will avoid deadlocks. Basically when the second users submits a proposal for the same Aggregate Root the db will not let you read it until the first transaction has committed.
You should also consider adding a db index on Proposal.RequestForProposalId, it will help performance as that is the column the Proposal's are loaded on. I'm not 100% if it helps minimize the scope of any locks on that table, but it might ...