Unit of Work is definitely the way to go. If you're using SQL Server with a local database, TransactionScope
will do most of the heavy lifting for you; as long as you share sessions between repositories (which you're doing through constructor injection, right...?), then you can nest these to your heart's content. By default, it enlists in the "ambient" transaction if there is one, and starts a new one if there isn't, which is exactly the behaviour you want for a unit of work.
So your repositories might look like this:
public class UserRepository : IUserRepository
{
public UserRepository(ISession session)
{
this.Session = session;
}
public void Save(User user)
{
using (TransactionScope tsc = new TransactionScope())
{
Session.Save(user);
tsc.Complete();
}
}
protected ISession Session { get; set; }
}
public class OrderRepository : IOrderRepository
{
public OrderRepository(ISession session)
{
this.Session = session;
}
public void Save(Order order)
{
using (TransactionScope tsc = new TransactionScope())
{
Session.Save(order);
tsc.Complete();
}
}
protected ISession Session { get; set; }
}
Then you can perform the complete unit of work like so:
User currentUser = GetCurrentUser();
using (TransactionScope tsc = new TransactionScope())
{
ISession session = SessionFactory.OpenSession();
Order order = new Order(...);
order.User = currentUser;
IOrderRepository orderRepository = GetOrderRepository(session);
orderRepository.Save(order);
currentUser.LastOrderDate = DateTime.Now;
IUserRepository userRepository = GetUserRepository(session);
userRepository.Save(currentUser);
tsc.Complete();
}
If you don't like TransactionScope
or your environment prevents you from using it effectively then you can always implement your own UOW or use an existing implementation. Or if you're just an architectural neat-freak then you can do both - use a generic unit-of-work interface with one of the main DI libraries, and implement your concrete UOW using the TransactionScope
.