包含在(半)通用存储库中

本文关键字:存储 包含 | 更新日期: 2023-09-27 17:53:25

我正试图弄清楚如何使用新的EF Code First的东西,我有麻烦弄清楚如何适应Include功能到一个半通用的存储库类。(我说半泛型,是因为类不是泛型的,只有方法。所以我有一个存储库包装聚合,本质上,你可以与所有实体的一部分,聚合,而不仅仅是一个单一的实体进行交互。

我的情况是我有一个实体,它有一个子实体,总是需要加载,然后其他子实体只能选择性地加载,我想在我的存储库方法上包含一个简单的bool参数。像这样:

public S GetByID<S>(int entityID, bool loadChildren = false) where S : class
{
  DbSet<S> set = _context.Set<S>();
  if (loadChildren)
    set = FullInclude<S>(set);
  else
    set = DefaultInclude<S>(set);
  return set.Find(entityID);
}
protected virtual DbSet<S> DefaultInclude<S>(DbSet<S> set) where S : class
{
  // base implementation just returns the set
  // derived versions would attach Includes to the set before returning it
  return set;
}
protected virtual DbSet<S> FullInclude<S>(DbSet<S> set) where S : class
{
  // base implementation just returns the set
  // derived versions would attach Includes to the set before returning it
  return set;
}

这是我的基础存储库,我希望能够在派生类中重写那些XyzInclude方法。但我需要用非泛型实现覆盖它们,这显然在语言级别不起作用。

使用Linq2Sql非常容易,因为我只需配置一个DataLoadOptions对象并将其附加到上下文。随着DbSetInclude API关闭,我对如何最好地做到这一点感到困惑。我正在寻找关于策略模式或类似模式的简单实现的建议。

EDIT:我想我的问题的本质是关于用非泛型派生版本重写泛型方法。我在寻找一种模式或技术,让我能做到这一点。扩展方法是我正在考虑的一件事,但如果有人有一个更"纯粹"的解决方案,我更喜欢一个。

包含在(半)通用存储库中

除非你使类完全泛型,否则不可能,:

public class BaseRepo<S>
{
    protected virtual DbSet<S> DefaultInclude(DbSet<S> set) {return set;}
} 
public class ProductRepo : BaseRepo<Product>
{
    protected override DbSet<Product> DefaultInclude(DbSet<Product> set)
    {
       return set.Include("...");
    }
}

我只是想用我最终得到的解决方案来重新审视这个问题。我的存储库不是完全通用的,但是所有的方法都是通用的,所以我需要一种方法来存储任何实体类型的包含,并且能够在为该类型调用方法时将它们拉出来。

我在这里借用了Ladislav的答案,我可能会重新考虑这个设计,只让派生存储库上定义的每个单独的方法定义它们自己的包含,因为包含什么和不包含什么有足够多的不同组合,在几个地方重复一小段代码定义相同的包含可能是值得的。但无论如何,这是目前的设计,它工作…

基础存储库:

public abstract class Repository : IQueryableRepository, IWritableRepository
{
  private readonly DbContext _context;
  private readonly Dictionary<Type, LambdaExpression[]> _includes = new Dictionary<Type, LambdaExpression[]>();
  protected Repository(DbContextBase context)
  {
    _context = context;
    RegisterIncludes(_includes);
  }
  protected abstract void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes);
  protected S GetSingle<S>(Expression<Func<S, bool>> query, bool getChildren = false) where S : class
  {
    IQueryable<S> entities = _context.Set<S>().AsNoTracking();
    if (query != null)
      entities = entities.Where(query);
    entities = ApplyIncludesToQuery<S>(entities, getChildren);
    return entities.FirstOrDefault();
  }
  private IQueryable<S> ApplyIncludesToQuery<S>(IQueryable<S> entities, bool getChildren) where S : class
  {
    Expression<Func<S, object>>[] includes = null;
    if (getChildren && _includes.ContainsKey(typeof(S)))
      includes = (Expression<Func<S, object>>[])_includes[typeof(S)];
    if (includes != null)
      entities = includes.Aggregate(entities, (current, include) => current.Include(include));
    return entities;
  }
}

派生的存储库只需要在一个地方定义它们的包含,然后当你调用查询方法时,你只需要指定你是否想要包含孩子(参见上面的getChildren)。

public class DerivedRepository : Repository
{
  public DerivedRepository(DbContext context)
    : base(context) { }
  protected override void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes)
  {
    includes.Add(typeof(ParentType), new Expression<Func<ParentType, object>>[] { 
      p => p.SomeChildReference.SomeGrandchild,
      p => p.SomeOtherChildReference
    });
  }
}