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.
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