Skip to content

Instantly share code, notes, and snippets.

@Kaushik1987
Forked from azborgonovo/UnitOfWork.cs
Created October 3, 2019 10:02
Show Gist options
  • Save Kaushik1987/b2dbc76d388564cbc6e9e34fe417e31c to your computer and use it in GitHub Desktop.
Save Kaushik1987/b2dbc76d388564cbc6e9e34fe417e31c to your computer and use it in GitHub Desktop.
Creating the 'best' Unit of Work and Repository implementation on C#
// Basic unitOfWork pattern as described by Martin Fowler (http://martinfowler.com/eaaCatalog/unitOfWork.html)
// Other methos as 'registerNew' are going to be managed by each repository
public interface IUnitOfWork : IDisposable
{
void Commit();
Task CommitAsync();
void Rollback();
}
public interface IUnitOfWorkFactory
{
IUnitOfWork CreateUnitOfWork();
IUnitOfWork CreateUnitOfWork(bool beginDatabaseTransaction);
}
public interface IDbSetFactory
{
IDbSet<T> CreateDbSet<T>() where T : class;
}
// TODO: include async
public interface IReadOnlyRepository<T> where T : class
{
IEnumerable<T> GetAll(Func<T, bool> predicate = null);
T Get(Func<T, bool> predicate);
}
public interface IRepository<T> : IReadOnlyRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Remove(T entity);
}
public abstract class Repository<T> : IRepository<T>
{
readonly IDbSetFactory _dbSetFactory;
public Repository(IDbSetFactory dbSetFactory)
{
_dbSetFactory = dbSetFactory;
}
}
public abstract class EntityFrameworkContext : DbContext, IUnitOfWorkFactory, IDbSetFactory
{
public IDbSet<T> CreateDbSet<T>() where T : class
{
return Set<T>();
}
public IUnitOfWork CreateUnitOfWork()
{
return CreateUnitOfWork(false);
}
public IUnitOfWork CreateUnitOfWork(bool beginDatabaseTransaction)
{
return new EntityFrameworkUnitOfWork(this, beginDatabaseTransaction);
}
}
public class EntityFrameworkUnitOfWork : IUnitOfWork
{
bool _usesDatabaseTransaction;
DbContext _dbContext;
DbContextTransaction _dbContextTransaction;
public EntityFrameworkUnitOfWork(DbContext dbContext, bool beginDatabaseTransaction)
{
_dbContext = dbContext;
_usesDatabaseTransaction = beginDatabaseTransaction;
if (_usesDatabaseTransaction)
_dbContextTransaction = _dbContext.Database.BeginTransaction();
}
public void Commit()
{
_dbContext.SaveChanges();
if (_usesDatabaseTransaction && _dbContextTransaction != null)
_dbContextTransaction.Commit();
}
public void Rollback()
{
if (_usesDatabaseTransaction && _dbContextTransaction != null)
_dbContextTransaction.Rollback();
}
public void Dispose()
{
if (_dbContextTransaction != null)
_dbContextTransaction.Dispose();
}
}
// IUnitOfWork.cs
public interface IUnitOfWork : IDisposable
{
void Commit();
Task CommitAsync();
}
// ITenant.cs
public interface ITenant
{
public string ConnectionString { get; }
public string Schema { get; }
}
// IKonexiaContext.cs
public interface IKonexiaContext
{
IUnitOfWork CreateCoreUnitOfWork();
IUnitOfWork CreateTenantUnitOfWork(ITenant tenant);
}
// KonexiaContext.cs
public abstract class KonexiaContext : IKonexiaContext
{
public IUnitOfWork CreateCoreUnitOfWork()
{
return new EntityFrameworkUnitOfWork(null);
}
public IUnitOfWork CreateTenantUnitOfWork(ITenant tenant)
{
return new EntityFrameworkUnitOfWork(tenant);
}
}
// EntityFrameworkUnitOfWork.cs
public class EntityFrameworkUnitOfWork : DbContext, IUnitOfWork
{
ITenant _tenant;
public EntityFrameworkUnitOfWork(tenant)
: base(tenant == null ? "DefaultConnection" : tenant.ConnectionString)
{
_tenant = tenant;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
if (tenant != null && !string.IsNullOrEmpty(tenant.Schema))
{
//Configure default schema
modelBuilder.HasDefaultSchema(tenant.Schema);
}
}
public void Commit()
{
this.SaveChanges();
}
public void CommitAsync()
{
this.SaveChangesAsync();
}
}
// ---------------------------------
// Usage
// ---------------------------------
// UsuariosAppService.cs
public class UsuariosAppService : IUsuariosAppService
{
IKonexiaContext _context;
public UsuariosAppService(IKonexiaContext context)
{
_context = context;
}
public void CriarUsuario(UsuarioDto dto)
{
var usuario = Mapper.Map<Usuario>(dto); // just for clarification
using (var uow = _context.CreateCoreUnitOfWork())
{
var usuariosRepository = uow.Repository<IUsuariosRepository>();
usuariosRepository.Add(usuario);
uow.Commit();
}
}
public void CriarAlgoComTenant(ITenant tenant, Algo algo)
{
using (var uow = _context.CreateTenantUnitOfWork(tenant))
{
var algosRepository = uow.Repository<IAlgosRepository>();
algosRepository.Add(algo);
uow.Commit();
}
}
}
public interface ISalesContext : IUnitOfWorkFactory, IDbSetFactory
{
IClientsRepository ClientsRepository { get; }
}
public class SalesContext : EntityFrameworkContext, ISalesContext
{
readonly Func<IClientsRepository> _clientsRepository;
public IClientsRepository ClientsRepository { get { return _clientsRepository(); } }
public SalesContext(Func<IClientsRepository> clientsRepository)
{
_clientsRepository = clientsRepository;
}
}
public interface IClientsRepository : IRepository<Client> { }
public class ClientsRepository : Repository<Client>, IClientsRepository
{
public ClientsRepository(ISalesContext salesContext)
: base(salesContext)
{
}
}
public class TestContext : ISalesContext
{
readonly Func<IClientsRepository> _clientsRepository;
public IClientsRepository ClientsRepository { get { return _clientsRepository(); } }
public TestContext(Func<IClientsRepository> clientsRepository)
{
_clientsRepository = clientsRepository;
}
public IDbSet<T> CreateDbSet<T>() where T : class
{
return new TestDbSet<T>(); // TODO: put the sets into static for state maintenance
}
public IUnitOfWork CreateUnitOfWork()
{
return new TestUnitOfWork();
}
public void Dispose() { }
}
// Application Service sample usage
public class ClientsService
{
readonly ISalesContext _salesContext;
public ClientsService(ISalesContext salesContext)
{
_salesContext = salesContext;
}
public IEnumerable<ClientDto> GetAllClients()
{
// This method doesn't need to init a Unit of Work
// since it isn't making any business transaction
var clients = _salesContext.ClientsRepository.GetAll();
// ...
return list;
}
public void CreateClient(ClientDto clientDto)
{
using (var unitOfWork = _salesContext.CreateUnitOfWork())
{
try
{
// ...
_salesContext.ClientsRepository.Add(client);
unitOfWork.Commit();
}
catch
{
unitOfWork.Rollback();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment