I have created an application (net core 2 & ef core) with Unit Of Work and Generic repository pattern. I used to have one database context but due to some business logic I had to create a second database with some same entities.
For the sake of simplicity, I will use only one Entity, the ProductEntity
and I will use only 1 repository method the Get by Id
.
In the business logic I must be able to retrieve the Product from the two contexts, do some stuff and then update the contexts, but with a clean UoW design.
The repository is implemented like this
public interface IRepository<TEntity>
where TEntity : class, new()
{
TEntity Get(int id);
}
public interface IProductsRepository : IRepository<ProductEntity>
{
}
public class ProductsRepository : Repository<ProductEntity>, IProductsRepository
{
public ProductsRepository(DbContext context) : base(context)
{
}
}
Implementation of UOW with one db context
public interface IUnitOfWork : IDisposable
{
IProductsRepository ProductsRepository { get; }
int Complete();
}
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(MainContext context)
{
// injecting the main database
_context = context;
}
private IProductsRepository _productsRepository;
public IProductsRepository ProductsRepository => _productsRepository ?? (_productsRepository = new ProductsRepository(_context));
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context?.Dispose();
}
}
I am using the default framework of .NET Core for DI, so at my Startup.cs
file I have the following
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// ...
// main database
services.AddDbContext<MainContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MainDatabaseConnection"), providerOptions => providerOptions.CommandTimeout(30)));
// unit of work
services.AddTransient<IUnitOfWork, UnitOfWork>();
}
To solve the problem I have created a second UnitOfWork with hardcoded context and I am using the same entities/repositories
My implementation with two db contexts
public interface IUnitOfWorkSecondary : IDisposable
{
IProductsRepository ProductsRepository { get; }
int Complete();
}
public class UnitOfWorkSecondary : IUnitOfWorkSecondary
{
private readonly DbContext _context;
public UnitOfWork(SecondaryDatabaseContext context)
{
// injecting the secondary database
_context = context;
}
// same as above
}
So in a business object I am doing the following
public class Program
{
private readonly IUnitOfWork _unitOfWork;
private readonly IUnitOfWorkSecondary _unitOfWorkSecondary;
public Program(IUnitOfWork unitOfWork, IUnitOfWorkSecondary unitOfWorkSecondary){
_unitOfWork = unitOfWork;
_unitOfWorkSecondary = unitOfWorkSecondary;
}
public static void Method1(int productId)
{
var mainProduct = _unitOfWork.ProductsRepository.Get(productId);
var secondaryProduct = _unitOfWorkSecondary.ProductsRepository.Get(productId);
mainProduct.Name = "Hello Main";
secondaryProduct.Name = "Hello Secondary";
_unitOfWork.Complete();
_unitOfWorkSecondary.Complete();
}
}
The Startup.cs
is modified to
// main database
services.AddDbContext<MainContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MainDatabaseConnection"), providerOptions => providerOptions.CommandTimeout(30)));
// secondary database
services.AddDbContext<SecondaryContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SecondaryDatabaseConnectiont"), providerOptions => providerOptions.CommandTimeout(30)));
// unit of work
services.AddTransient<IUnitOfWork, UnitOfWork>();
services.AddTransient<IUnitOfWorkSecondary , UnitOfWorkSecondary>();
My questions
- What is the best (or most practical) design for this
- What if the databases are more than 2 ?