Skip to main content
edited title; edited tags
Source Link
toolic
  • 16.4k
  • 6
  • 29
  • 221

Generic UnitOfWork implementation. C#, PostgreSQL, Dapper (Dapper)

Generic UnitOfWork implementation. C#, PostgreSQL, Dapper (Dapper)

Generic UnitOfWork implementation. C#, PostgreSQL, Dapper

Generic UnitOfWork implementation (Dapper)

added dependency injection snippet
Source Link
services.AddScoped<IDbConnection>(provider =>
{
    var connectionString = configuration.GetConnectionString("DefaultConnection");
    return new NpgsqlConnection(connectionString);
});

services.AddScoped<IUnitOfWork, UnitOfWork.UnitOfWork>();
services.AddScoped<IDbConnection>(provider =>
{
    var connectionString = configuration.GetConnectionString("DefaultConnection");
    return new NpgsqlConnection(connectionString);
});

services.AddScoped<IUnitOfWork, UnitOfWork.UnitOfWork>();
Source Link

Generic UnitOfWork implementation. C#, PostgreSQL, Dapper

I'm creating a three layered Visual Studio template solution that I'll be using as a starting point for all ASP.NET projects. The layers are Presentation, Application and Infrastructure. IUnitOfWork and IRepository are declared in the Application layer. UnitOfWork is implemented in the Infrastructure layer and it's injected through dependency injection container.

public interface IUnitOfWork : IDisposable
{
    IDbTransaction Transaction { get; }
    TRepository? Repository<TRepository>() where TRepository : class;
    void Commit();
    void Rollback();
    void AddRepository(IRepository repository);
}

public interface IRepository
{
    void SetTransaction(IDbTransaction transaction);
}

public class UnitOfWork : IUnitOfWork
{
    private readonly IDbConnection _connection;
    private readonly IServiceProvider _serviceProvider;
    private IDbTransaction _transaction;
    private bool _disposed;
    private readonly List<IRepository> _repositories = new();

    public IDbTransaction Transaction => _transaction;

    public UnitOfWork(IDbConnection dbConnection, IServiceProvider serviceProvider)
    {
        _connection = dbConnection;
        if (_connection.State != ConnectionState.Open) _connection.Open();
        _transaction = _connection.BeginTransaction();
        _serviceProvider = serviceProvider;
    }

    public TRepository? Repository<TRepository>() where TRepository : class
    {
        var repository = _serviceProvider.GetService<TRepository>();

        if (repository is IRepository transactionAware)
        {
            transactionAware.SetTransaction(_transaction);
            AddRepository(transactionAware);
        }
        return repository;
    }

    public void Commit()
    {
        try
        {
            _transaction.Commit();
        }
        catch
        {
            throw;
        }
        finally
        {
            UpdateTransaction();
        }
    }

    public void Rollback()
    {
        _transaction.Rollback();
        UpdateTransaction();
    }

    private void UpdateTransaction()
    {
        _transaction = _connection.BeginTransaction();
        foreach (var repository in _repositories)
        {
            if (repository is IRepository transactionAware)
            {
                transactionAware.SetTransaction(_transaction);
            }
        }
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _transaction?.Rollback();
            _transaction?.Dispose();
            _connection?.Dispose();
            _disposed = true;
        }
    }

    public void AddRepository(IRepository repository)
    {
        if (_repositories.Contains(repository)) return;
        _repositories.Add(repository);
    }
}

Usage

public class MyService
{
    private readonly IMyRepository _myRepository;
    private readonly IUnitOfWork _unitOfWork;

    public MyService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        _myRepository = unitOfWork.Repository<IMyRepository>();
    }
}