【问题标题】:Includes in a (semi) generic Repository包含在(半)通用存储库中
【发布时间】:2011-04-27 05:41:58
【问题描述】:

我试图弄清楚如何使用新的 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 对象并将其附加到上下文中。使用Include API 关闭DbSet,我不知道如何最好地做到这一点。我正在寻找有关策略模式或类似模式的简单实现的建议。

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

【问题讨论】:

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


    【解决方案1】:

    除非你使类完全通用,否则这是不可能的:

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

    【讨论】:

    • 我更多的是寻找与上述不同的解决方案。我不能使存储库完全通用。我在前一个版本中拥有完全通用的存储库,并且由于各种原因不得不转向非通用方向。另外,我需要能够为任何实体设置包含。我的存储库设计不是很多人采用的半典型的单个存储库每个实体。不过感谢您的回答。我很感激。
    • 我只能说你的要求在你目前的设计中根本不可能。您可能需要重新考虑整个概念。
    【解决方案2】:

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

    我借用了 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
        });
      }
    }
    

    【讨论】:

    • 这是一种有趣的方法,但我不太喜欢的是包含在一个位置。我遇到的问题是,根据情况,您可能想要不同的包括,例如在获取分页客户列表时,您不需要个人资料、帖子和发票信息......但是对于用户详细信息页面,您确实需要所有信息
    • 是的,这就是我在脑海中争论的两难境地,试图找出最好的方法。我现在选择了最简单的选项(在一个地方定义它们),但进化的设计将让每个方法定义自己的包含,并将其作为参数发送到基本存储库,而不是作为类级变量.我在上面链接到的 Ladislav 的答案涵盖了这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-10
    • 2019-05-24
    • 2010-10-01
    • 2023-03-11
    • 1970-01-01
    • 2020-09-28
    • 2022-08-19
    相关资源
    最近更新 更多