Linq Projection to DTO for Cested / Hierarchical Collections

时间:2016-03-01 11:54:46

标签: c# entity-framework linq

我有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; }
}

从概念上讲,ProductPermittedUsers的集合 - 即可以购买该产品的用户。此外,每个User都有一个PermittedProducts的集合,以及一组子用户,他们也拥有自己的PermittedProducts集合。

我需要通过存储库运行查询以返回产品列表。存储库方法和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; }
  }

存储库方法需要带一个Guid userId并为该用户检索PermittedProducts 为用户的孩子PermittedProducts

例如,如果产品可供用户及其两个孩子使用,那么ProductListDto将拥有该用户集合中的所有三个用户。

作为另一个例子,如果某个产品不适用于某个用户,但该产品可供他的孩子使用,那么这也需要返回。

ProductUser都可用作聚合根,因此我可以使用ProductRepositoryUserRepository来查询,通过EntityFramework&#39; s { {1}}。

目前我的存储库方法位于DbSet(但如果查询更简单,可以移至UserRepository),如下所示:

ProductRepository

我的问题是我无法弄清楚如何编写Linq查询来实现我需要做的事情!

修改

到目前为止,答案并没有解决如何实现对 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! } 的投射

2 个答案:

答案 0 :(得分:1)

我如何处理这个问题只是从父UserId建立一个Id列表,这将包含Parent UserId及其所有ChildId。然后,我们可以从此列表中选择PermittedUsers包含其中一个Id的产品。您可以在此处获取产品列表。

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

答案 1 :(得分:0)

试试这个

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

线路说明   - 1)检索目标用户及其子女   - 2)从第一行的用户集合中选择所有产品   - 3-4)删除重复项。

注意这样的linq可以被翻译成可以降低性能的重sql查询。也许最好在行(1,2或3)之后调用ToList。还可以编写自己的SQL查询,将它们存储在sql server中,并按名称从代码中调用它们。