【问题标题】:Use skip and take inside a LINQ include在 LINQ 包含中使用 skip 和 take
【发布时间】:2016-12-24 13:12:48
【问题描述】:

我有一个对象,它的属性是另一个对象的集合。我想使用 LINQ 加载集合属性的一个子集。

这是我的尝试:

manager = db.Managers
            .Include(m => m.Transactions.Skip((page - 1) * 10).Take(10))
            .Where(m => m.Id == id)
            .FirstOrDefault();

上面的代码抛出一个错误,上面写着

包含路径表达式必须引用在类型上定义的导航属性。对引用导航属性使用虚线路径,对集合导航属性使用 Select 运算符。\r\n参数名称:路径

在 LINQ 中执行此操作的正确方法是什么?提前致谢。

【问题讨论】:

  • 对于给定的经理,平均而言是否有很多事务?换句话说,您对性能的关注程度如何?如果不是太多,您可以进行预加载,然后正常使用 Skip 和 Take 进行过滤。
  • @BrunoSaboia 感谢您的回复。是的。经理可以有很多事务。加载所有事务会减慢应用程序的速度。我相信我已经在使用急切加载,但我不太确定“使用跳过并正常进行过滤”。我该怎么做?
  • Rian,你不是在使用急切加载,而是在使用延迟加载。急切加载将加载给定管理器的所有事务。我看到您可能不想使用急切加载,因为每个经理有很多事务。我会考虑一个尊重您需求的解决方案,然后回到这里
  • @BrunoSaboia .Include 表达式是预加载的定义。 msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx 延迟加载将只调用 manager.Transactions 而不包含。 Rian:是的,请禁用延迟加载。这是性能测试的噩梦,这是 IMO 一些人认为 EF 速度慢的最大原因。特别是如果您与经验不足的同事一起工作。如何禁用延迟加载可以在这里找到:stackoverflow.com/questions/2967214/…
  • @BrunoSaboia 我已经关闭了延迟加载。这就是我使用 .include 的原因。 hvd 在下面发布了一个可行的解决方案。

标签: c# linq


【解决方案1】:

您不能使用包含来执行此操作。 EF 根本不知道如何将其转换为 SQL。但是你可以用子查询做类似的事情。

manager = db.Managers
            .Where(m => m.Id == id)
            .Select(m => new { Manager = m, 
                               Transactions = db.Transactions
                                                .Where(t=>t.ManagerId == m.Id)
                                                .Skip((page-1) * 10)
                                                .Take(10)})
            .FirstOrDefault();

这不会返回 Manager 类的实例。但它应该很容易修改以满足您的需求。

您还有另外两个选择:

  1. 加载所有事务,然后在内存中过滤。当然,如果有很多交易,这可能会非常低效。
  2. 不要害怕在数据库中进行 2 次查询。这是最好的例子,这可能是最好的路线,并且可能是最有效的方式。

无论哪种方式,如果您完全关心性能,我建议您测试所有 3 种方法,看看哪种方法最快。请告诉我们结果如何!

【讨论】:

  • 我尝试了您的第一个解决方案,但无法正常工作。我使用了 hvd 之前也建议的其他 2 个选项中的第二个,并让它工作。感谢您的解决方案,但 hvd 首先发布了工作解决方案,所以我必须接受他的答案作为正确答案。不过还是非常感谢。
【解决方案2】:

有时将所有内容放在一个查询中所增加的复杂性是不值得的。我会将其拆分为两个单独的查询:

var manager = db.Managers.SingleOrDefault(m => m.Id == id);
var transactions = db.Transactions
    .Where(t => t.ManagerId == id)
    // .OrderBy(...)
    .Skip((page - 1) * 10).Take(10)
    .ToList();

请注意,在这样做之后,manager.Transactions 也可以用来指代那些刚刚加载的事务:只要将加载的实体加载到相同的上下文中,Entity Framework 就会自动链接它们。只需确保禁用延迟加载,以防止 EF 自动拉入您特别尝试过滤掉的所有其他事务。

【讨论】:

  • 我实际上考虑过这种方法,但我认为将所有内容放在一个查询中将是最有效的方法。好吧,我想我错了。这个解决方案对我有用!非常感谢!
猜你喜欢
  • 2012-06-12
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
  • 2012-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-03
相关资源
最近更新 更多