【问题标题】:c# linq OrderByDescending with inner LastOrDefault [duplicate]c# linq OrderByDescending 与内部 LastOrDefault [重复]
【发布时间】:2020-02-12 11:59:13
【问题描述】:

有没有办法在最后一次购买日期之前订购所有客户?

例如

ctx.Customers.OrderByDescending(e => e.Purchases.LastOrDefault().DateTime);

应该是这样的,但是,这不起作用。它抛出异常

LINQ to Entities does not recognize the method 'Purchase
LastOrDefault[Purchase]
(System.Collections.Generic.IEnumerable`1[Purchase])'
method, and this method cannot be translated into a store expression

编辑:

public class Customer
{
    public Customer()
    {
        Purchases = new List<Purchase>();
    }

    public int Id { get; set; }
    public string Name { get; set; }

    [JsonIgnore]
    public virtual IList<Purchase> Purchases { get; set; }
}

public class Purchase
{
    public int Id { get; set; }
    public int IdCustomer { get; set; }
    public DateTime DateTime { get; set; }

    public virtual Customer Customer { get; set; }
}

在上下文中我确实有类似的东西

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.HasRequired(s => s.Customer)
            .WithMany(p => p.Purchases)
            .HasForeignKey(s => s.IdCustomer);
}

【问题讨论】:

  • 编辑:对不起,我打错了。即使 LastOrDefault 是方法,错误仍然存​​在。
  • 你能显示一些代码吗?持有Purchases 属性的类和持有DateTime 属性的类?
  • 什么是e.Purchases
  • @Çöđěxěŕ 它是购买的 IList
  • @CorentinPane 我可以,我展示的代码应该可以工作?

标签: c# mysql linq


【解决方案1】:

LastOrDefault 在 Linq-to-Entities 中是 not supported(这意味着他们还没有开发出一种将其转换为等效 SQL 代码的方法)。一种选择是使用AsEnumerable 在内存中进行排序:

ctx.Customers
   .AsEnumerable()
   .OrderByDescending(e => e.Purchases.LastOrDefault().DateTime);

但是,由于Purchases 的顺序不是确定性的,您可能还想在那里指定一个顺序:

ctx.Customers
   .AsEnumerable()
   .OrderByDescending(e => e.Purchases.OrderBy(p => p.DateTime).LastOrDefault());

或者只是在“购买”上使用Max

ctx.Customers
   .AsEnumerable()
   .OrderByDescending(e => e.Purchases.Max(p => p.DateTime));

如果其中任何查询的性能不可接受,最后的办法是编写直接 SQL 并将其传递给 ctx.Customers.SqlQuery()

【讨论】:

  • 但是这样效率极低。
  • @FilipCordas 你怎么知道它会比等效的 SQL 效率低得多?即使是,你提出什么替代方案?
  • @FilipCordas 只是可能......如果是为了合理数量的记录,IQueryable 应该没问题。
  • 首先我认为如果没有包含但我不确定是否应该测试,这将在单独的查询中提取每个“购买”。但是,如果他已经吸引了所有客户,那么您可能是对的,这可能就足够了。
  • @FilipCordas 它应该仍然在一个查询中提取所有客户和购买,因为只有订购(而不是过滤)是在内存中完成的。
【解决方案2】:
ctx.Customers.OrderByDescending(e => e.Purchases.LastOrDefault().DateTime);

看起来像一个上下文查询(实体框架,通常是 dbContext),所以这里你有一个 IQueryable 而不是一个列表。

Entity Framework 会在给你结果之前尝试将其转换为 SQL 语句,但是

SELECT * BOTTOM(X) FROM TABLE ORDER BY Purchases desc

不是表达式,但更重要的是 EF 只是无法识别您想要做什么。

相反,您只想将逻辑翻转为:

SELECT * TOP(X) FROM TABLE ORDER BY Purchases asc

或者:

ctx.Customers.OrderBy(e => e.Purchases.FirstOrDefault().DateTime);

或者你可以在你的子查询中排序:

ctx.Customers.OrderBy(e => e.Purchases.OrderByDescending(x => x.propertyToSortOn)
.FirstOrDefault().DateTime);

从一个排序列表的底部获取最后 n 条记录,实际上与从另一个排序的列表中获取顶部 n 条记录相同:

1,2,3,4,5,6 -> 升序前 3 = 1,2,3

6,5,4,3,2,1 -> 降序后 3 = 3,2,1

【讨论】:

  • 对此我不确定。相关的枚举是否在 EF 中排序?当您多次运行查询时,这可能会返回不同的行,具体取决于 SQL 服务器不了解 MySQL。
  • @FilipCordas 你是对的,但代码也是。您需要总是指定排序列。 EF 会尝试猜测默认值,至少有时是这样。但是当您需要依赖它时,它还不够可靠。使用上一个示例中的内部 OrderByDescending 将在 EF 6.x 中至少转换为内部 Select * top(x) from t order by [ENTITY2].[X] desc 或类似内容。
  • 您的答案是正确的,只是认为值得一提,因为除非他确实订购,否则这将有一个错误。第一个和最后一个一样随机,所以他的查询没有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-03
  • 2019-10-08
  • 1970-01-01
  • 2011-03-28
  • 1970-01-01
  • 2014-05-20
  • 1970-01-01
相关资源
最近更新 更多