【问题标题】:Where should I put the complex queries using the Repository Pattern?我应该使用存储库模式将复杂查询放在哪里?
【发布时间】:2022-02-12 23:17:03
【问题描述】:

我有一个使用 Entity Framework 的应用程序,我有一个名为 BaseRepository<T> 的类,其中包含一些基本的 CRUD 方法,例如(Get、GetAll、Update、Delete、Insert),并从这个类中生成我的特定存储库,例如BaseRepository <Products>BaseRepository<People>BaseRepository<Countries> 等等。

问题是,当我在服务中有一个复杂的逻辑时,它涉及连接多个表并且不返回实体,而是在服务中处理的对象(它既不是数据库实体,也不是一个 DTO),我发现存储库仅对基本的 CRUD 操作没有多大帮助。

我应该把这个复杂的查询放在哪里?它应该在哪个存储库中?如何加入这些存储库?问题是我看到存储库处理单个实体,在这种情况下我该怎么办?我一直在做一些研究,发现返回 IQueryable<T> 是不好的做法,所以我排除了发送 IQueryable<T> 的可能性,我将加入该服务并在那里执行。

我进行了研究,但没有找到明确的答案。我想知道这些复杂查询的组织方式和位置,因为我还想尊重每个存储库及其各自实体的责任。

【问题讨论】:

  • 简短回答:不在通用存储库(反)模式中。对于长答案,请参阅我对这个问题的回答:stackoverflow.com/questions/70760405/…
  • 存储库只不过是一个数据库表,作为具有列表语义的对象公开给对象模型。正如DbSet 已经做得足够了。
  • “我一直在做一些研究,发现返回 IQueryable 是不好的做法” 不。就是这样,你刚刚发现了原因。让数据消费者请求它想要的任何形状。

标签: c# entity-framework design-patterns architecture repository-pattern


【解决方案1】:

我想知道这些复杂查询的组织方式和位置,因为我还想尊重每个存储库及其各自实体的责任。

复杂查询是请求数据的代码的责任,而不是存储库的责任。存储库的唯一职责是提供对数据的访问,而不是提供任何用例中可能需要的每个可能的请求形式。您真的想在您的存储库中编写方法,例如:

customerRepo.GetCustomerWithLastTenOrdersAndSupportCasesAsDTO()

customerRepo.GetCustomerForCustomerSupportHomePage()

当然不是。因此,您的存储库提供了一个 IQueryable<T>DbSet<T> 类型的属性,可以用作消费者添加他们需要的任何查询的起点。

我一直在做一些研究,发现返回 IQueryable 是不好的做法

不要相信你读到的一切。从您的存储库中公开IQueryable<T> 确实没有一个好的选择。一旦你消化了这一点,除了你的 DbContext 子类型之外,没有太多理由拥有任何存储库类型。

【讨论】:

    【解决方案2】:

    如果没有代码来理解您想要实现的目标,很难回答,希望我的回答能让您了解如何使用抽象类来覆盖您的可查询集合。如果您的要求更复杂,您能否提供更多信息以及示例代码。

    像这样创建你的 BaseRepository -

    public abstract class BaseRepository<T>
    {
        public IQueryable<T> Collection { get; set; }
        public readonly DbContext _context;
    
        public BaseRepository(DbContext context)
        {
            _context = context;
            Collection = SetQueryableCollection();
        }
    
        public virtual IQueryable<T> SetQueryableCollection() => _context.Set<T>();
    
        // CRUD Operations here for e.g. -
        public virtual async Task<List<T>> Get()
        {
            return await Collection.ToListAsync();
        }
    }
    

    现在,继承这个的类 -

    public class ProductRepository : BaseRepository<Product>
    {
        public ProductRepository(MyContext context) : base(context)
        {
        }
    
        //now override the method that sets the collection
        public override IQueryable<Product> SetQueryableCollection() => 
            _context.Set<Product>().Include(p => p.Brand).ThenInclude(...);
    
        // things to keep in mind, the _context is public in the way I've done it. You can change that and directly expose the Collection and set it to your need per entity type.
    }
    

    所以现在您的 GET 方法使用覆盖的方法来设置集合。

    【讨论】:

      猜你喜欢
      • 2020-06-19
      • 1970-01-01
      • 1970-01-01
      • 2015-01-13
      • 1970-01-01
      • 2014-06-07
      • 2017-11-06
      • 1970-01-01
      • 2018-10-28
      相关资源
      最近更新 更多