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

时间:2011-04-27 05:41:59

标签: c# ef-code-first eager-loading

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

我的情况是我有一个实体,其中有一个总是需要加载的子项,然后是其他只加载的子项,我想在我的存储库方法中包含一个简单的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对象并将其附加到上下文中。关于Include API DbSet,我很难理解如何做到这一点。我正在寻找关于策略模式或类似方法的简单实现的建议。

编辑:我想我的问题的本质是关于用非泛型派生版本覆盖泛型方法。我正在寻找一种允许我这样做的模式或技术。扩展方法是我正在研究的一件事,但如果有人有一个更纯粹的解决方案。

2 个答案:

答案 0 :(得分:0)

除非你让这个类完全通用,否则不可能:

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

答案 1 :(得分:0)

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

我借用了Ladislav的答案here,我可能会重新审视这个设计,只是让派生的存储库中定义的每个单独的方法都定义了自己的包含,因为有足够的不同组合包含什么和不包括什么在一些地方重复定义相同的代码的一点点代码可能是值得的。但无论如何,这是当前的设计,它的工作原理......

基础存储库:

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