3

J'ai besoin de conserver une propriété calculée avec une racine agrégée. Le calcul est basé sur des entités enfants. J'utilise la racine pour ajouter/supprimer les enfants via des méthodes de domaine, et ces méthodes mettent à jour la propriété calculate.Comment gérer la concurrence avec des propriétés calculées persistantes dans une racine agrégée via NHibernate?

Une entité enfant peut être ajoutée à une racine particulière par plusieurs utilisateurs du système. Par exemple, UserA peut ajouter un enfant à Root123, et UserB peut également ajouter un enfant à Root123.

Comment puis-je m'assurer que cette propriété calculée est conservée avec précision lorsque plusieurs utilisateurs peuvent ajouter des entités enfants à la même racine dans différentes transactions? Dans mon cas particulier, la propriété calculée est utilisée pour s'assurer que certaines limites ne sont pas dépassées, comme défini par une autre propriété sur la racine.


Voici un exemple plus concret du problème:

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--; 
    } 
} 

Et si 2 utilisateurs soumettent leurs propositions à peu près en même temps? L'interface utilisateur voit que la limite n'a pas encore été atteinte et affiche la page Web pour soumettre une proposition pour les deux utilisateurs. Lorsque le premier utilisateur soumet, tout va bien. Maintenant, le deuxième utilisateur ira bien tant que le premier utilisateur soumet avant le second, de sorte que lorsque le second utilisateur soumet, les données sont extraites du DB et la limite sera précise.

Est-ce un point discutable? Devrais-je m'appuyer sur des contraintes dans la base de données (ProposalLimit> = ProposalCount) pour les cas rares où 2 utilisateurs soumettent presque simultanément?

+0

Je n'ai jamais eu le besoin, mais cela semble être une application pour les verrous de bases de données. – Kendrick

+0

J'ai considéré les verrous, mais je ne savais pas si cela en valait la peine. – ventaur

Répondre

2

Si vous deviez mettre en place la vérification des règles métier (c.-à-d. Contrôle limite) dans une transaction, tout ira bien. C'est

  1. utilisateur clique bouton qui déclenche la commande Ajouter Proposition
  2. code commence une nouvelle transaction. Voir here for how I suggest you use transactions
  3. Charger l'objet RequestForProposal à partir de la base de données ou l'actualiser. Je vous suggère d'utiliser un verrou de mise à niveau.
  4. Ajoutez les nouvelles propositions à la racine. Vérifiez la contrainte de limite, lancez une exception, elle échoue.
  5. valider la transaction

En procédant ainsi, vous utilisez les bases de données concurrency controls. Je ne pense pas qu'il existe un autre moyen de le faire.

Cela créera une certaine contention, mais vous pouvez prendre quelques mesures pour minimiser cela. À savoir assurez-vous à l'étape 3 que la colonne db que vous sélectionnez a un index dessus. Cela entraînera un verrouillage de ligne, au lieu d'un verrouillage de page.

Si vous utilisez un verrou de mise à niveau à l'étape 3, cela évite les blocages. Fondamentalement, lorsque les deuxièmes utilisateurs soumettent une proposition pour la même racine d'agrégat, la base de données ne vous laissera pas la lire jusqu'à ce que la première transaction ait été validée.

Vous devriez également envisager d'ajouter un index db sur Proposal.RequestForProposalId, cela aidera les performances car c'est la colonne sur laquelle les propositions sont chargées. Je ne suis pas à 100% si cela permet de minimiser la portée de tous les verrous sur cette table, mais il pourrait ...

+0

C'était une bonne réponse et j'ai négligé d'indiquer cela plus tôt. Je vous remercie. – ventaur

Questions connexes