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