【问题标题】:How to "let" in lambda expression?如何在 lambda 表达式中“让”?
【发布时间】:2012-03-03 15:58:40
【问题描述】:

如何使用 lambda 表达式将此 linq 查询重写为实体?
我想在我的 lambda 表达式中使用 let 关键字或等效关键字。

var results = from store in Stores
              let AveragePrice =  store.Sales.Average(s => s.Price)
              where AveragePrice < 500 && AveragePrice > 250

对于一些类似的问题,比如我的问题下评论了什么,建议

.Select(store=> new { AveragePrice = store.Sales.Average(s => s.Price), store})

它将计算每个项目的AveragePrice,而在我提到的查询样式中,let 表达式会阻止多次计算平均值。

【问题讨论】:

标签: linq entity-framework c#-4.0 lambda let


【解决方案1】:

因此,您可以使用扩展方法语法,这将涉及比您当前使用的多一个 lambda 表达式。没有let,你只需使用多行lambda并声明一个变量:

var results = Stores.Where(store => 
{
    var averagePrice = store.Sales.Average(s => s.Price);
    return averagePrice > 250 && averagePrice < 500;
});

请注意,我更改了平均价格比较,因为您的将永远不会返回任何结果(超过 500 且少于 250)。

另一种选择是

var results = Stores.Select(store => new { Store = store, AveragePrice = store.Sales.Average(s => s.Price})
    .Where(x => x.AveragePrice > 250 && x.AveragePrice < 500)
    .Select(x => x.Store);

【讨论】:

  • 错误:带有语句体的 lambda 表达式无法转换为表达式树。
  • 您的第一个报价仅适用于内存,无法在 EF 等 LINQ 提供程序中使用。带有语句体的 lambda 表达式不能转换为表达式树。
  • @amkh:我几乎可以肯定问题的第一个版本中没有提到 EF。或者至少杰伊和我都没有注意到……
  • @Reza:每个商店对象只计算一次平均值,与您的查询完全相同...
  • @Reza 不,我不希望它一定会提高 EF 查询性能。答案就是如何使用扩展方法语法进行类似let 的操作。为什么不坚持使用查询语法呢?
【解决方案2】:

基本上,您需要使用 Select 和匿名类型将平均值添加到您的对象,然后是您的语句的其余部分。

未经测试,但应该如下所示:

Stores.Select(
x => new { averagePrice = x.Sales.Average(s => s.Price), store = x})
.Where(y => y.averagePrice > 500 && y.averagePrice < 250)
.Select(x => x.store);

警告,小心这些结构。使用 let 为集合中的每个对象创建一个新的匿名类型,它会在大型集合中消耗大量内存 ...

详情请看这里: Let in chained extension methods

【讨论】:

  • 如果是 LINQ-to-Entities 查询或类似查询,它不会消耗 任何 内存。
  • 确实,仅适用于 linq-to-objects。谢谢
  • 这应该是公认的答案,因为它可以让您在不同的“子句”中使用您的变量。
【解决方案3】:

另一种选择是定义这个扩展方法:

public static class Functional
{
    public static TResult Pipe<T, TResult>(this T value, Func<T, TResult> func)
    {
        return func(value);
    }
}    

然后像这样编写您的查询:

var results = Stores
    .Where(store => store.Sales.Average(s => s.Price)
        .Pipe(averagePrice => averagePrice < 500 && averagePrice > 250));

【讨论】:

    【解决方案4】:

    我们可以通过 inline out 声明避免在所有其他答案中使用的 lambda 的开销:

    public static class FunctionalExtensions
    {
        public static T Assign<T>(this T o, out T result) =>
            result = o;
    }
    

    然后这样称呼它

    var results = Stores
        .Where(store => store.Sales
            .Average(s => s.Price)
            .Assign(out var averagePrice) < 500 && averagePrice > 250);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多