I'm working on a web api project decoupled and the bussiness logic its decoupled in extensions (separated projects, that gives me a lot of shared code between projects), thats why I'm working on a data layer also decoupled, everything its working but the only thing that keeps me everything coupled its AppDbContext.cs
Here is a POC code so you can get my idea (my problem):
AppDbContext.cs
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> dbContextOptions) : base(dbContextOptions)
{
}
}
IEntity.cs
public interface IEntity<TKey>
{
TKey Id { get; set; }
}
IRepository.cs
public interface IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
IEnumerable<TEntity> GetAll();
}
GenericRepository.cs
public class GenericRepository<TEntity, TKey> : IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
private readonly AppDbContext dbContext;
public GenericRepository(AppDbContext dbContext)
{
this.dbContext = dbContext;
}
public IEnumerable<TEntity> GetAll()
{
return dbContext.Set<TEntity>().ToList();
}
}
and register it in the composition root like this:
services.AddScoped(typeof(IRepository<,>), typeof(GenericRepository<,>));
As you can see, my generic repository uses AppDbContext, but what if in a different project that is called different ? or inherits from IdentityContext, how can I make my Generic Repository, DbContext independient but also configurable at startup ?
Update:
I forgot to mention that, in some cases there will be more than one DbContext implementation.
Lowest common factor here is DbContext.
Rafactor GenericRepository to explicitly depend on DbContext
public class GenericRepository<TEntity, TKey> : IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey> {
private readonly DbContext dbContext;
public GenericRepository(DbContext dbContext) {
this.dbContext = dbContext;
}
public IEnumerable<TEntity> GetAll() {
return dbContext.Set<TEntity>().ToList();
}
}
At composition root you would then make the association
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration["database:connectionString"]));
services.AddScoped(typeof(IRepository<,>), typeof(GenericRepository<,>));
services.AddScoped<DbContext, AppDbContext>();
In case of multiple Contexts, That would require a little more abstraction. In cases like that I create a specific abstraction for each context. like IDbContext or ILoggingContext
public interface IDbContext : IDisposable {
int SaveContext();
DbSet<TEntity> Set<TEntity>();
//...other relevant EF members, etc
}
public interface IAppDbContext : IDbContext {
}
public interface ILogDbContext : IDbContext {
}
and have my DbContext derived classes inherit from the one relevant to it.
public class AppDbContext : DbContext, IAppDbContext {
public AppDbContext(DbContextOptions<AppDbContext> dbContextOptions) : base(dbContextOptions) {
}
}
public class LogDbContext : DbContext, ILogDbContext {
public AppDbContext(DbContextOptions<LogDbContext> dbContextOptions) : base(dbContextOptions) {
}
}
From there the generic repository would explicitly depend on the relevant abstraction(s)
public class GenericRepository<TEntity, TKey> : IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey> {
private readonly IDbContext dbContext;
public GenericRepository(IAppDbContext dbContext) {
this.dbContext = dbContext;
}
//...code removed for brevity
}
and then do the necessary configuration at composition root.
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration["database:appConnectionString"]));
services.AddDbContext<LogDbContext>(options =>
options.UseSqlServer(Configuration["database:logConnectionString"]));
services.AddScoped(typeof(IRepository<,>), typeof(GenericRepository<,>));
services.AddScoped<IAppDbContext, AppDbContext>();
services.AddScoped<ILogDbContext, LogDbContext>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With