Where should the transaction boundary be in a repository pattern?

烈酒焚心 提交于 2019-12-07 17:58:35

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.

Transactions should not be applied to repositories, because if more than one needs to participate in a transaction there's no way to say it.

Have a separate service tier that uses repositories and model objects to satisfy use cases. The service methods know where the transaction boundaries need to be. That's where they should be applied.

One of the options is the unit of work pattern. This is already explained in several previous questions.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!