Search code examples
c#interfaceentity-framework-coredomain-driven-designdbcontext

DbContext interface in DDD


I read that Domain project shouldn't specify any ORM. So if I have to create interface for DbContext implemented in Infrastructture project, how can I do it? How can I specify all required DbSet? Interface in Domain project:

public interface IConfigurationDbContext
{
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}

Implementation in Infrastructure project:

public class ConfigurationDbContext : DbContext, IConfigurationDbContext
{
    public ConfigurationDbContext([NotNull] DbContextOptions<ConfigurationDbContext> options) : base(options)
    {
    }

    public DbSet<Client> Clients { get; set; }
    public DbSet<ApiResource> ApiResources { get; set; }
    public DbSet<ApiScope> ApiScopes { get; set; }
    public DbSet<IdentityResource> IdentityResources { get; set; }
}

As u see, IConfigurationDbContext interface doesn't contain any DbSet, because this way require to specify used ORM in Domain project. So how should I create this interface?


Solution

  • Use IQueryable to create a full-featured abstraction over your DbContext. This preserves the core query-building capabilities of the DbContext, and is easilly substituable by another type using Queryable.AsQueryable.

    public interface IConfigurationRepository
    {
        public IQueryable<Client> Clients { get; }
        public IQueryable<ApiResource> ApiResources { get; }
        public IQueryable<ApiScope> ApiScopes { get; }
        public IQueryable<IdentityResource> IdentityResources { get; }
    
        public void Add<TEntity>(TEntity e);
        public void Remove<TEntity>(TEntity e);
        public Task<int> SaveChangesAsync();
    }
    

    And you can implement this interface instead of having DbSet properties on your DbContext or you can use explicit interface implementation, eg:

        public IQueryable<Client> Clients => this.Set<Client>();
    
        public IQueryable<ApiResource> ApiResources => this.Set<ApiResource>();
    
        public IQueryable<ApiScope> ApiScopes => this.Set<ApiScope>();
    
        public IQueryable<IdentityResource> IdentityResources => this.Set<IdentityResource>();
    

    If you remove the DbSet properties, you need to declare your entity types in OnModelCreating like this:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<IdentityResource>();
        builder.Entity<ApiResource>();
        builder.Entity<ApiScope>();
        builder.Entity<Client>();
        . . .
    
     }
    

    And when you need the DbSet<TEntity> get it with

    dbContext.Set<TEntity>()