Link Search Menu Expand Document

Unit of Work


This page has the consolidated code of the Unit of Work reference implementation.

Interface

public interface IUnitOfWork<TDbConnection>
{
    TDbConnection Connection { get; }
    DbTransaction Transaction { get ; }
    void Begin();
    void Rollback();
    void Commit();
}

public interface IRepository<TEntity> where TEntity : class
{
    void Attach(IUnitOfWork unitOfWork);
    TResult Add<TResult>(TEntity entity);
    int AddAll<TResult>(IEnumerable<TEntity> entities);
    int Delete(object id);
    int Delete(Entity entity);
    TResult Merge<TResult>(TEntity entity);
    TEntity Query(object id);
    int Update(TEntity entity);
}

public interface IOrderRepository : IRepository<Order>
{
    /* No order specific methods for now */
}

public interface ISalesManager
{
    void SaveOrder(Order order,
        IEnumerable<OrderItem> orderItems);

    /* More business logic methods */
}

Class

public class RepoDbUnitOfWork : IUnitOfWork<SqlConnection>
{
    /* Privates */

    private AppSettings settings;
    private SqlConnection connection;
    private DbTransaction transaction;

    /* Constructors */

    public RepoDbUnitOfWork(IOptions<AppSettings> settings)
    {
        this.settings = settings.Value;
    }

    /* Properties */

    public SqlConnection Connection { get { return connection; } }
    
    public DbTransaction Transaction { get { return transaction; } }

    /* Custom Methods */

    private SqlConnection EnsureConnection() =>
        connection ?? connection = new SqlConnection(settings.ConnectionString);

    /* Interface Methods */

    public void Start()
    {
        if (transaction != null)
        {
            throw new InvalidOperationException("Cannot start a new transaction while the existing other one is still open.");
        }
        var connection = EnsureConnection();
        transaction = connection.BeginTransaction();
    }

    public void Commit()
    {
        if (transaction == null)
        {
            throw new InvalidOperationException("There is no active transaction to commit.");
        }
        using (transaction)
        {
            transaction.Commit();
        }
        transaction = null;
    }

    public void Rollback()
    {
        if (transaction == null)
        {
            throw new InvalidOperationException("There is no active transaction to rollback.");
        }
        using (transaction)
        {
            transaction.Rollback();
        }
        transaction = null;
    }
}

Repository


public class OrderRepository : BaseRepository<Order, SqlConnection>, IOrderRepository
{
    /* Privates */

    private UnitOfWork unitOfWork;

    /* Constructors */

    public OrderRepository(IOptions<Settings> settings)
        : base(settings.Value.ConnectionString)
    { }

    /* Interface Methods */

    public void Attach(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    public TResult Add<TResult>(Order entity)
    {
        return Insert<Order, TResult>(entity,
            transaction: unitOfWork?.Transaction);
    }

    public int AddAll(IEnumerable<Order> entities)
    {
        return InsertAll<Order>(entities,
            transaction: unitOfWork?.Transaction);
    }

    public int Delete(object id)
    {
        return Delete<Order>(id,
            transaction: unitOfWork?.Transaction);
    }

    public int Delete(Order entity)
    {
        return Delete<Order>(entity,
            transaction: unitOfWork?.Transaction);
    }

    public TResult Merge<TResult>(Order entity)
    {
        return Merge<Order, TResult>(entity,
            transaction: unitOfWork?.Transaction);
    }

    public TResult Query(object id)
    {
        return Query<Order>(id,
            transaction: unitOfWork?.Transaction);
    }

    public int Update(Order entity)
    {
        return Update<Order>(entity,
            transaction: unitOfWork?.Transaction);
    }
}

Business Logic

public class SalesManager : ISalesManager
{
    private IUnitOfWork unitOfWork;
    private IOrderRepository orderRepository;
    private IOrderItemRepository orderItemRepository;

    public SalesManager(IUnitOfWork unitOfWork,
        IOrderRepository orderRepository,
        IOrderItemRepository orderItemRepository,
        /* Other repositories here */)
    {
        // Attach the UOW
        orderRepository.Attach(unitOfWork);
        orderItemRepository.Attach(unitOfWork);
        /* Do the same for the other repositories */

        // Set the variables
        this.unitOfWork = unitOfWork;
        this.orderRepository = orderRepository;
        this.orderItemRepository = orderItemRepository;
        /* Do the same for the other repositories */
    }

    public void SaveOrders(Order order,
        IEnumerable<OrderItem> orderItems)
    {
        // Start the UOW
        unitOfWork.Begin();

        try
        {
            // Call the repository methods
            var orderId = orderRepository.Save(order);
            orderItems = orderItems
                .AsList()
                .ForEach(e => e.OrderId = orderId);
            orderItemRepository.SaveAll(orderItems);

            // Commit
            unitOfWork.Commit();
        }
        catch
        {
            // Rollback
            unitOfWork.Rollback();
            throw;
        }
    }
}

Dependency Injection

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    // Unit Of Work
    services.AddTransient<IUnitOfWork, RepoDbUnitOfWork>();

    // Repositories
    services.AddSingleton<IOrderRepository, OrderRepository>();
    services.AddSingleton<IOrderRepository, OrderRepository>();
    services.AddSingleton<IOrderItemRepository, OrderItemRepository>();
    /* Do the same for the other repositories */

    // Business Logics
    services.AddTransient<ISalesManager, SalesManager>();
}