净核心通用存储库模式如何在不知道其编译时不知道其类型的情况下注入DBContext



我正在研究一个已解耦的Web API项目,并且bussiness逻辑在扩展中脱钩(分开的项目,这给了我很多项目之间的共享代码),这就是为什么我是为什么在数据层上工作也脱钩,一切正常,但唯一让我耦合到其AppDbContext.cs

的东西是唯一的事情

这是一个POC代码,因此您可以得到我的想法(我的问题):

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();
    }
}

并像这样将其注册在构图根中:

services.AddScoped(typeof(IRepository<,>), typeof(GenericRepository<,>));

您可以看到,我的通用存储库使用appdbcontext,但是如果在一个被称为不同的项目中,该怎么办?或从IdentityContext继承,如何使我的通用存储库,DBContext独立但也可以在启动时配置?

更新:

我忘了提到,在某些情况下,将有一个以上的dbcontext实现。

最低的常见因素是 DbContext

Rafactor GenericRepository明确依赖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();
    }
}

在构图根上您将使关联

services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(Configuration["database:connectionString"]));
services.AddScoped(typeof(IRepository<,>), typeof(GenericRepository<,>));   
services.AddScoped<DbContext, AppDbContext>();

更新

在多种情况下,这需要更多的抽象。在这样的情况下,我为每个上下文创建一个特定的抽象。例如IDbContextILoggingContext

public interface IDbContext : IDisposable {
    int SaveContext();
    DbSet<TEntity> Set<TEntity>();
    //...other relevant EF members, etc
}
public interface IAppDbContext : IDbContext {
}
public interface ILogDbContext : IDbContext {
}

并让我的DbContext派生的类从与之相关的一个类别继承。

public class AppDbContext : DbContext, IAppDbContext {
    public AppDbContext(DbContextOptions<AppDbContext> dbContextOptions) : base(dbContextOptions) {
    }
}
public class LogDbContext : DbContext, ILogDbContext {
    public AppDbContext(DbContextOptions<LogDbContext> dbContextOptions) : base(dbContextOptions) {
    }
}

从那里,通用存储库将明确取决于相关的抽象

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
}

,然后在组成根上进行必要的配置。

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>();

我找到了一个更简单的解决方案(但不是更干净)。对于通知,我实际上不反对为ef使用存储库模式。

startup.cs

services.AddDbContext<context1>(options => options.UseSqlServer(Connection1), ServiceLifetime.Scoped);
services.AddDbContext<context2>(options => options.UseSqlServer(DbConnection2), ServiceLifetime.Scoped);
// Build an intermediate service provider
var sp = services.BuildServiceProvider();
services
    .AddScoped<IGenericRepository<Repository.Models.SubmissionEntry>>(_ => new GenericRepository<SomeSpecificModelThatUsesContext2>(sp.GetService<context2>(), true))
    .AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>))       

genericrepository.cs

just add second constructor         
private readonly DbContext dbContext;
public GenericRepository(context1 ctx)
{dbContext=ctx}
public GenericRepository(context2 ctx, bool fakeParamToAddSecondConstructor) 
{dbContext=ctx}

相关内容

最新更新