简体   繁体   English

Linq投影到DTO的嵌套/分层集合

[英]Linq Projection to DTO for Nested/Hierarchical Collections

I have a User and Product entities which are defined as follows: 我有一个UserProduct实体,它们的定义如下:

public class User {

    Guid Id { get; set; }
    Guid ParentId { get; set; }       
    ICollection<Product> PermittedProducts { get; set; }
    ICollection<User> Children { get; set; }
}

public class Product {

    int Id { get; set; }
    string Name { get; set; }
    ICollection<User> PermittedUsers { get; set; }
}

Conceptually, a Product has a collection of PermittedUsers - ie users that can purchase the product. 从概念上讲, Product具有PermittedUsers的集合-即可以购买产品的用户。 Additionally, each User has a collection of PermittedProducts , as well as a collection of child users, who also have their own collection of PermittedProducts . 此外,每个User都有一个PermittedProducts集合,还有一个子用户集合,这些子用户也有自己的PermittedProducts集合。

I need to run a query via a repository to return a list of products. 我需要通过存储库运行查询以返回产品列表。 The repository method and DTO are defined as: 存储库方法和DTO定义为:

  public ICollection<ProductListDto> GetProductsForUser(Guid userId) {
       // Linq query here
  }

  public class ProductListDto {

      int Id { get; set; }
      string Name { get; set; }
      ICollection<User> Users { get; set; }
  }

The repository method needs to take a Guid userId and retrieve the PermittedProducts for that User AND the PermittedProducts for the user's children. 该库方法需要采取一个GUID userId和检索PermittedProducts为用户 PermittedProducts用户的孩子。

For example, if a product is available for a user and his two children, then the ProductListDto would have all three users in it's users collection. 例如,如果某个用户及其两个孩子都可以使用某个产品,则ProductListDto将在其用户集合中拥有所有三个用户。

As a further example, if a product is not available for a user, but it is available for his children, then this would need to be returned as well. 作为进一步的示例,如果产品对用户不可用,但是对他的孩子可用,则也需要将其退回。

Both the Product and User are available as aggregate roots, so I can use either a ProductRepository or UserRepository to query through, via EntityFramework's DbSet . ProductUser都可以作为聚合根使用,因此我可以通过EntityFramework的DbSet使用ProductRepositoryUserRepository进行查询。

At the moment my repository method is in the UserRepository (but could move to the ProductRepository if the query is simpler) and looks like: 目前,我的存储库方法位于UserRepository (但如果查询更简单,则可以移至ProductRepository ),如下所示:

 public ICollection<ProductListDto> GetProductsForUser(Guid userId) {
       // Linq query here - Set is the EF DbSet<User> 
       var products = from u in 
           Set.Where(x => x.Id == userId) //.... NOT SURE ABOUT THE REST!

  }

My problem is I cannot work out how to write the Linq query to achieve what I need to do! 我的问题是我无法解决如何编写Linq查询来实现我需要做的事情!

EDIT 编辑

The answers so far don't address how to achieve the projection to the ProductListDto 到目前为止的答案还没有解决如何实现对ProductListDto的投影

How I would approach this is simply build a list of Id's from the parent UserId so this will contain the Parent UserId and all of it's ChildId's. 我将如何处理这只是从父UserId构建一个ID列表,因此它将包含父UserId及其所有ChildId。 From this list we can then select from Products where PermittedUsers contains one of these Id's. 然后,我们可以从此列表中选择其中PermittedUsers包含这些ID之一的产品。 This is where you can get the product list from. 您可以从这里获取产品列表。

        var childIds = DbContext.Users.Where(x => x.Id == userId).SelectMany(y => y.Children.Select(z => z.Id)).ToList();
        childIds.Add(userId);
        var products = DbContext.Products.Where(x => x.Users.SelectMany(y => childIds.Contains(y.Id))).ToList();

Try this 尝试这个

userRepository.Where(u => u.Id == userId && u.ParentId == userId)
            .SelectMany(u => u.PermittedProducts)
            .GroupBy(p => p.Id)
            .Select(u => u.First());

Line explanation - 1) Retrieves target user and his children - 2) Select all products from user collection we get in first line - 3- 4) Remove duplicates. 线路说明-1)检索目标用户及其孩子-2)从第一行获得的用户集中选择所有产品-3-4)删除重复项。

Note such linq can be translated into heavy sql query that can decrease performance. 请注意,此类linq可以转换为繁重的sql查询,这可能会降低性能。 Maybe it is better to call ToList after lines (1, 2 or 3). 也许最好在第(1、2或3)行之后调用ToList。 Also it is possible to write your own SQL query, store them in sql server and call them from code by name. 也可以编写自己的SQL查询,将其存储在sql服务器中,然后按名称从代码中调用它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM