C# Shared Transactions and NHibernate using IRepository
I'm looking into implementing the IRepository pattern using NHibernate and I have question that I've not been able to answer searching the net.
Assume I have 3 Repositories, PersonRepository, PersonAddressRepository and PersonAccountRepository. Now assume that business logic dictates that there be an "Deactivate Person" process that calls PersonRepository.Deactivate(), PersonAddressRepository.Deactivate() and PersonAccountRepository.Deactivate().
I want to be able to do something along the lines of..
using (ITransaction transaction = session.BeginTransaction()) {
session.Update(Person);
session.Update(PersonAddress);
session.Update(PersonAccount);
}
So that if any of those updates fail that the entire process rolls back within the database. Now at the moment my understanding of NHibernate is you can only create a Session per object so..
var cfg = new Configuration();
cfg.Configure();
cfg.AddAssembly(typeof(Person).Assembly);
ISessionFactory sessionFactory = cfg.BuildSessionFactory();
using (ISession session = sessionFactory.OpenSession()) {
using (ITransaction t开发者_高级运维ransaction = session.BeginTransaction()) {
session.Save(Person);
}
Is this correct or am I mistaken? What are the best practices for Transactions regarding multi table updates and Transactions with regards to NHibernate.
Thanks in advance.
You should not create transactions in the repositories or somewhere else "bellow". Transactions are defined by the application logic. This is one of the most common mistakes I see in transaction handling.
I wrote a transaction service which manages the transactions:
using (TransactionService.CreateTransactionScope())
{
repositoryA.DoX();
repositoryB.DoY();
TransactionService.Commit();
}
The repository is getting the session with an open transaction from the service:
TransactionService.Session.CreateQuery("...");
Depending on your environment, you need to make it a bit more complicated. For instance, the session may not be visible to the business logic and should to be put onto another interface etc.
I thought NHibernate understands the System.Transactions.TransactionScope class. Why wouldn't you use that?
One thing you can do -this is how I do it right now- is pass the ISession instance that should be used to your repository instances.
What I will do, in the future, is this:
I have a
UnitOfWork
class, which is quite generic and is a wrapper around NHibernate'sISession
object. This UnitOfWork class contains no 'application' or domain specific methodsIn a project which uses NHibernate (and my UnitOfWork wrapper), I'll create a set of extension methods on the
UnitOfWork
class, that look like this:public static class UnitOfWorkExtension` { public static IPersonRepository GetPersonRepository( this UnitOfWork uow) { return new PersonRepository(uow); } public static IAccountRepository GetAccountRepository( this UnitofWork uow ) { return new AccountRepository(uow); } }
Then, this would allow me to do this, for instance:
using( var uow = unitOfWorkFactory.CreateUnitOfWork() )
{
var person = uow.GetPersonRepository().GetPerson (1);
var accounts = uow.GetAccountRepository().GetAccountsForPerson(person);
}
But, looking at your example, I'm wondering whether you should have a repository for 'PersonAddress' and 'PersonAccount'.
In my humble opinion, Person
is an 'aggregate root' which consists of a PersonAddress
and a PersonAccount
in your example, and there should be a PersonRepository
which handles the Person aggregate root (including the PersonAddress and PersonAccount objects - which are, in fact not entities but value objects (as I see it)).
精彩评论