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 ?