问题
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 transaction = 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.
回答1:
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.
回答2:
I thought NHibernate understands the System.Transactions.TransactionScope class. Why wouldn't you use that?
回答3:
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)).
来源:https://stackoverflow.com/questions/5026994/c-sharp-shared-transactions-and-nhibernate-using-irepository