【问题标题】:How to use Skip() and Take() for some DbSet<Class>'s property which is a ICollection<>如何对某些 DbSet<Class> 的 ICollection<> 属性使用 Skip() 和 Take()
【发布时间】:2020-06-21 10:38:23
【问题描述】:

我有一个 DbSet 类:

public class Manufacturer
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
    public virtual Category Category { get; set; }
    public virtual ICollection<Product> Products { get; set; }
}

我知道我可以使用Skip()Take() 来限制manufacturers。但我的要求是在所有manufacturers 中获得有限的Products。我正在使用类似的东西,但它不起作用

var manufacturers = await _context.Manufacturers.Where(x => x.Products.Take(10))
                    .ToListAsync(); 

PS:我正在使用延迟加载(不是急切加载)

编译错误是:

不能隐式转换类型 'System.Collections.Generic.IEnumerable' 到 'bool' 无法将 lambda 表达式转换为预期的委托类型,因为 块中的某些返回类型不可隐式转换 到委托返回类型

我怎样才能获得所有制造商但有限的产品?

【问题讨论】:

  • 什么不起作用?也许你忘了使用Include
  • 我正在使用 LazyLoading
  • @PavelAnikhouski 用编译器错误更新了问题
  • 您的具体要求是什么?只获得 10 种产品的制造商? 10 是多少?
  • 我想你想要像await _context.Manufacturers.Select(m =&gt; { m.Products = m.Products.Take(10); return m; }).ToListAsync(); 这样的东西,但我怀疑它在 EF 中可能是这样的。

标签: c# asp.net-core entity-framework-core


【解决方案1】:

我相信没有办法直接使用可查询的源来执行此操作。你可以在内存中管理它。

var 制造商 = await _context.Manufacturers.Include(m => m.Products).ToListAsync();

foreach(制造商中的 var m)
{
    m.Products = m.Products.Take(10).ToList();
}

这将从数据库中获取每个制造商的所有产品,然后只保留前 10 个。

【讨论】:

  • 感谢您的回复。但是你能建议一种我不必从数据库中获取所有产品的方法吗?我可以为制造商提供数千种产品,但我最多只想获取 10 个
  • 我不确定 EF 是否可以,但如果我发现任何东西,我会更新我的答案。
【解决方案2】:

您可以在没有Product 列表的情况下先加载Manufacturer 实体(因此无需Include() 调用),然后运行单独的查询以仅加载特定Manufacturer 实体所需的产品。 EF 将自动更新导航属性。看下面的例子(这个例子中作者可以有多个帖子):

using (var context = new MyContext())
{
    Author author = context.Author.First();
            
    Console.WriteLine(context.Post.Where(it => it.Author == author).Count());
            
    context.Post.Where(it => it.Author == author).Take(2).ToList();
            
    Console.WriteLine(author.Posts.Count());
}

这将生成以下输出:

3
2

尽管我的测试数据库中有三个可用的条目,但实际上只读取了两个。查看生成的 SQL 查询:

对于Author author = context.Author.First(); 行:

SELECT `a`.`Id`, `a`.`Name`
FROM `Author` AS `a`
LIMIT 1

对于context.Post.Where(it =&gt; it.Author == author).Count() 行:

SELECT COUNT(*)
FROM `Post` AS `p`
INNER JOIN `Author` AS `a` ON `p`.`AuthorId` = `a`.`Id`
WHERE `a`.`Id` = 1

对于context.Post.Where(it =&gt; it.Author == author).Take(2).ToList(); 行:

SELECT `p`.`Id`, `p`.`AuthorId`, `p`.`Content`
FROM `Post` AS `p`
INNER JOIN `Author` AS `a` ON `p`.`AuthorId` = `a`.`Id`
WHERE `a`.`Id` = 1
LIMIT 2

但是,您必须为每个单独的 Manufacturer 实体执行此技巧,它只加载十个关联的 Product 实体。这可能会导致1+N SELECT queries

【讨论】:

    【解决方案3】:

    尝试更长的路:

    _await _context.Manufacturers.Select(x => 
    {
      x.Products = x.Products.Take(10).ToList();
      return x;
    }).ToListAsync();
    

    【讨论】:

    • 感谢您的回复。但是它没有与编译器警告一起工作:因为不等待此调用,所以在调用完成之前继续执行当前方法。考虑将“等待”运算符应用于调用结果。 [应用]csharp(CS4014) 带有语句体的 lambda 表达式无法转换为表达式树 [应用]csharp(CS0834) 表达式树可能不包含赋值运算符
    猜你喜欢
    • 2020-12-31
    • 1970-01-01
    • 1970-01-01
    • 2016-10-03
    • 2013-11-28
    • 2015-04-28
    • 2010-10-20
    • 1970-01-01
    相关资源
    最近更新 更多