4

J'essaie de déterminer comment terminer l'implémentation du modèle Repository dans une application Web ASP.NET. À l'heure actuelle, j'ai une interface de référentiel par classe de domaine définissant des méthodes pour, par ex. chargement et sauvegarde des instances de cette classe.Comment passer un conteneur d'unité de travail dans le constructeur du référentiel à l'aide de l'injection de dépendance

Chaque interface de référentiel est implémentée par une classe qui fait le NHibernate. Castle Windsor trie le DI de la classe dans l'interface selon web.config. Un exemple d'une classe mis en oeuvre est présentée ci-dessous:

public class StoredWillRepository : IStoredWillRepository 
    { 
    public StoredWill Load(int id) 
    { 
     StoredWill storedWill; 
     using (ISession session = NHibernateSessionFactory.OpenSession()) 
     { 
     storedWill = session.Load<StoredWill>(id); 
     NHibernateUtil.Initialize(storedWill); 
     } 
     return storedWill; 
    } 

    public void Save(StoredWill storedWill) 
    { 
     using (ISession session = NHibernateSessionFactory.OpenSession()) 
     { 
     using (ITransaction transaction = session.BeginTransaction()) 
     { 
      session.SaveOrUpdate(storedWill); 
      transaction.Commit(); 
     } 
     } 
    } 
    } 

Comme indiqué dans un fil précédent, la classe de stockage doit accepter une unité de récipient de travail (à savoir ISession) plutôt que de l'instanciation dans chaque procédé.

Je prévois que le conteneur d'unité de travail sera créé par chaque page ASPX lorsque cela est nécessaire (par exemple, dans une propriété).

Comment puis-je spécifier que cette instance de conteneur d'unité de travail doit être transmise au constructeur de StoredWillRepository lorsque Windsor le crée pour moi?

Ou est-ce complètement faux?

Merci encore pour vos conseils.

David

Répondre

0

Techniquement, la réponse à ma question est d'utiliser la surcharge de container.Resolve qui vous permet de spécifier l'argument du constructeur comme un type anonyme:

IUnitOfWork unitOfWork = [Code to get unit of work]; 
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork }); 

Mais avouons-le, les réponses fournies par tous d'autre ont été beaucoup plus informatif.

+0

C'est, comme vous le dites, techniquement la réponse à ma question, même si ce n'est pas la meilleure solution architecturale. Je suis vert-ticking vous. – David

0

J'ai une structure assez semblable à la vôtre, et voici comment je résous votre question:

1) Pour spécifier mon conteneur sur chaque méthode, j'ai une classe séparée (« SessionManager ») que j'appelle ensuite via une propriété statique. Ce faisant, voici un exemple en utilisant mon implémentation Economie:

private static ISession NHibernateSession 
{ 
    get { return SessionManager.Instance.GetSession(); } 
} 

public T Save(T entity) 
{ 
    using (var transaction = NHibernateSession.BeginTransaction()) 
    { 
     ValidateEntityValues(entity); 
     NHibernateSession.Save(entity); 

     transaction.Commit(); 
    } 

    return entity; 
} 

2) Mon conteneur n'est pas créé sur chaque page ASPX. J'instancie toutes mes qualités NHibernate sur la page global.asax.

** Quelques autres choses naissent **

3) Vous n'avez pas besoin d'avoir une aide pour instancier la charge. Vous pouvez aussi bien utiliser Get au lieu de Load. Plus d'informations @Difference between Load and Get. 4) En utilisant votre code actuel, vous devrez répéter à peu près le même code pour chaque objet de domaine dont vous avez besoin (StoredWillRepository, PersonRepository, CategoryRepository, etc.), ce qui semble être un glisser. Vous pouvez très bien utiliser un generic class pour fonctionner sur NHibernate, comme:

public class Dao<T> : IDao<T> 
{ 
    public T SaveOrUpdate(T entity) 
    { 
     using (var transaction = NHibernateSession.BeginTransaction()) 
     { 
      NHibernateSession.SaveOrUpdate(entity); 
      transaction.Commit(); 
     } 

     return entity; 
    } 
} 

Dans ma mise en œuvre, je pourrais alors utiliser something like:

Service<StoredWill>.Instance.SaveOrUpdate(will); 
+0

Merci beaucoup pour votre contribution. Je vais répondre à vos points dans les commentaires numérotés ci-dessous: – David

+0

1) Une ISession statique? À moins d'avoir mal compris, cela signifierait que l'ISession (qui n'est pas thread-safe) est partagée entre tous les threads de l'application - très dangereux. Est-ce pas correct? – David

+0

2) Conteneur pour quoi? Désolé, je ne vois pas ce que tu veux dire. – David

1

J'ai un framework de persistance construit sur NHibernate qui est utilisé dans quelques applications Web. Il cache l'implémentation NH derrière une interface IRepository et IRepository<T>, avec les instances concrètes fournies par Unity (donc je pourrais en théorie échanger NHibernate, par exemple, Entity Framework assez facilement).Comme Unity ne supporte pas (ou du moins la version que j'utilise) le passage de paramètres constructeurs autres que ceux qui sont eux-mêmes des injections de dépendances, le passage dans un NHSession existant n'est pas possible; mais je veux que tous les objets de l'UOW partagent la même ISession.

je résous en ayant une classe référentiel de contrôle qui gère l'accès à l'ISession sur une base par thread:

public static ISession Session 
    { 
     get 
     { 
      lock (_lockObject) 
      { 
       // if a cached session exists, we'll use it 
       if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
       { 
        return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY]; 
       } 
       else 
       { 
        // must create a new session - note we're not caching the new session here... that's the job of 
        // BeginUnitOfWork(). 
        return _factory.OpenSession(new NHibernateInterceptor()); 
       } 
      } 
     } 
    } 

Dans cet exemple, PersistenceFrameworkContext.Current.Items accède à un IList<object> qui est stocké soit ThreadStatic sinon dans un Contexte Web ou HttpContext.Current.Items s'il se trouve dans un contexte Web (pour éviter les problèmes de pool de threads). Le premier appel à la propriété instancie le ISession à partir de l'instance d'usine stockée, les appels suivants le récupèrent simplement du stockage. Le verrouillage va ralentir légèrement les choses, mais pas autant que de verrouiller une instance statique ISession portée par un domaine.

J'ai alors BeginUnitOfWork et EndUnitOfWork méthodes pour prendre soin de l'UOW - J'ai spécifiquement interdit UOWs imbriqués parce que franchement, ils étaient une douleur à gérer.

public void BeginUnitOfWork() 
    { 
     lock (_lockObject) 
     { 
      if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
       EndUnitOfWork(); 

      ISession session = Session; 
      PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session); 
     } 
    } 

    public void EndUnitOfWork() 
    { 
     lock (_lockObject) 
     { 
      if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
      { 
       ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY]; 
       PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY); 
       session.Flush(); 
       session.Dispose(); 
      } 
     } 
    } 

Enfin, une paire de méthodes permettent d'accéder aux référentiels spécifiques à un domaine de type: (. Ici, PersistentObject<T> est une classe de base fournissant ID et equals support)

public IRepository<T> For<T>() 
     where T : PersistentObject<T> 
    { 
     return Container.Resolve<IRepository<T>>(); 
    } 

    public TRepository For<T, TRepository>() 
     where T : PersistentObject<T> 
     where TRepository : IRepository<T> 
    { 
     return Container.Resolve<TRepository>(); 
    } 

L'accès à un référentiel donné est donc dans le modèle

NHibernateRepository.For<MyDomainType>().Save(); 

Ceci est alors facaded sur ce que vous pouvez utiliser

MyDomainType.Repository.Save(); 

Lorsqu'un type donné a un dépôt spécialisé (c.-à-besoin plus qu'elle ne peut obtenir de IRepository<T>) puis je crée une interface provenant de IRepository<T>, une implémentation extension héritant de ma IRepository<T> mise en œuvre et dans le type de domaine lui-même je remplacer la propriété Repository statique à l'aide new

new public static IUserRepository Repository 
    { 
     get 
     { 
      return MyApplication.Repository.For<User, IUserRepository>(); 
     } 
    } 

(MyApplication [qui est appelé quelque chose de moins noddy dans le produit réel] est une classe de façade qui prend soin de supplyi ng l'instance Repository par unité de sorte que vous ne dépendent pas de la mise en œuvre du référentiel NHibernate spécifique dans vos classes de domaine.)

Cela me donne enfichabilité complète via l'unité pour la mise en œuvre du référentiel, un accès facile au référentiel dans le code sans sauter à travers des cerceaux , et transparent, gestion par thread ISession.

Il y a beaucoup plus de code que ce qui est au-dessus (et j'ai beaucoup simplifié l'exemple de code), mais vous avez l'idée générale.

MyApplication.Repository.BeginUnitOfWork(); 
User user = User.Repository.FindByEmail("[email protected]"); 
user.FirstName = "Joe"; // change something 
user.LastName = "Bloggs"; 
// you *can* call User.Repository.Save(user), but you don't need to, because... 
MyApplication.Repository.EndUnitOfWork(); 
// ...causes session flush which saves the changes automatically 

Dans mon application Web, j'ai session par demande, si BeginUnitOfWork et EndUnitOfWork s'appelle à BeginRequest et EndRequest respectivement.

+0

C'est génial. Je vais certainement vérifier si mon code pourrait utiliser une partie de votre bonté. xD – rebelliard

+0

Stack Overflow nécessite un bouton pour 'Probablement la bonne réponse, mais pour être franc, je ne le comprends pas'. Je serais en cliquant ici. Je vais essayer de digérer tout au long du week-end, mais il se peut que ce soit la deuxième meilleure façon de se saouler. Au moins, j'ai apprécié votre adresse e-mail! – David

+0

S'enivrer est une excellente idée que je vais copier plus tard ce soir :-) Quant au code, tous les niveaux d'indirection et de métaprogrammation rendent la lecture plus difficile. La mise en œuvre réelle et complète de notre cadre de persistance est sur le code le plus complexe dans notre arsenal ici - mais une fois que vous l'avez fait une fois, vous n'avez jamais besoin de le faire à nouveau. C'est payé pour lui-même plusieurs fois. –