2

J'ai une table User et une table ClubMember dans ma base de données. Il y a un mappage un-à-un entre les utilisateurs et les membres du club, donc chaque fois que j'insère un ClubMember, je dois d'abord insérer un User. Ceci est implémenté avec une clé étrangère sur ClubMember (UserId REFERENCES User (Id)).Besoin de conseils sur un scénario d'insertion complexe à l'aide de LinqToSql avec le modèle de référentiel

Dans mon application ASP.NET MVC, j'utilise LinqToSql et le modèle de référentiel pour gérer ma logique de persistance. De la façon dont j'ai actuellement implémenté cela, mes transactions User et ClubMember sont traitées par des classes de référentiel distinctes, chacune utilisant sa propre instance DataContext.

Cela fonctionne très bien s'il n'y a pas d'erreurs de base de données, mais je suis préoccupé par le fait que je serai à User enregistrements orphelins si des insertions ClubMember échouent.

Pour résoudre ce problème, j'envisage de passer à un seul DataContext, que je pourrais charger avec les deux inserts, puis appeler le DataContext.SubmitChanges() une seule fois. Le problème avec ceci, cependant, est que le Id pour User n'est pas assigné jusqu'à ce que le User soit inséré dans la base de données, et je ne peux pas insérer un ClubMember jusqu'à ce que je connaisse le UserId.

Questions:

  1. Est-il possible d'insérer la User dans la base de données, obtenir le Id, puis insérez le ClubMember, tout comme une seule transaction (qui peut être annulée en cas de problème avec une partie de la transaction)? Si oui, comment? Si ce n'est pas le cas, mon seul recours est-il de supprimer manuellement les enregistrements User orphelins qui ont été créés? Ou y a-t-il un meilleur moyen?

Répondre

1

Vous pouvez utiliser System.Transactions.TransactionScope pour effectuer cela dans une transaction atomique, mais si vous utilisez différentes instances DataContext, il en résultera dans une transaction distribuée, ce qui n'est probablement pas ce que vous voulez vraiment. Par le son, vous n'implémentez pas vraiment le modèle de référentiel correctement. Un référentiel ne doit pas créer son propre DataContext (ou objet de connexion, ou toute autre chose) - ces dépendances doivent être transmises via un constructeur ou une propriété publique. Si vous faites cela, vous aurez aucun problème à partager le DataContext:

public class UserRepository 
{ 
    private MyDataContext context; 

    public UserRepository(MyDataContext context) 
    { 
     if (context == null) 
      throw new ArgumentNullException("context"); 
     this.context = context; 
    } 

    public void Save(User user) { ... } 
} 

Utilisez le même schéma pour ClubMemberRepository (ou tout ce que vous appelez), ce qui est une formalité:

using (MyDataContext context = new MyDataContext()) 
{ 
    UserRepository userRep = new UserRepository(context); 
    userRep.Save(user); 
    ClubMemberRepository memberRep = new ClubMemberRepository(context); 
    memberRep.Save(member); 
    context.SubmitChanges(); 
} 

Bien sûr , même ce est un peu iffy. Si vous avez une clé étrangère dans votre base de données, vous ne devriez même pas avoir besoin de deux dépôts, car Linq to SQL gère la relation.Le code pour créer devrait simplement ressembler à ceci:

using (MyDataContext context = new MyDataContext()) 
{ 
    User user = new User(); 
    user.Name = "Bob"; 
    user.ClubMember = new ClubMember(); 
    user.ClubMember.Club = "Studio 54"; 

    UserRepository userRep = new UserRepository(context); 
    userRep.Save(user); 
    context.SubmitChanges(); 
} 

Ne pas jouer avec plusieurs référentiels - laisser LINQ to SQL gérer la relation pour vous, c'est ce que ORM sont pour.

+0

Aaron, bonne réponse, merci. Je me donne des coups de pied parce que j'avais initialement quelque chose de similaire à votre troisième extrait de code, mais au cours du débogage, j'en suis arrivé à la conclusion (incorrecte) que ça ne marcherait pas. Essayer à nouveau maintenant, cela fonctionne très bien. Deuxièmement, mes classes de dépôt sont basées sur NerdDinner, donc je blâme Scott Gu pour ma mise en œuvre moins que stellaire :). J'ai en fait un "todo" pour commencer à encapsuler mes instances 'DataContext' avec des instructions' using() ', et votre exemple me donne une bonne idée de la façon de procéder. – devuxer

0

Oui, vous pouvez le faire en une seule transaction. Utilisez l'objet TransactionScope pour commencer et valider la transaction (et revenir en arrière s'il y a une erreur bien sûr)

Questions connexes