lambda中的If语句用于整个查询
本文关键字:查询 用于 语句 中的 If lambda | 更新日期: 2023-09-27 18:16:03
这个语句可以写成1个lambda表达式吗?
if (filter.SubFormId.HasValue)
{
query = query.Where(c => c.SubFormId == filter.SubFormId);
}
对于这种查询要非常小心。where子句绑定到变量过滤器,而不是绑定到当前值。
原始代码:var query = from form in forms select form;
var filter = new Filter();
filter.SubFormId = 123;
if (filter.SubFormId.HasValue)
{
query = query.Where(c => c.SubFormId == filter.SubFormId);
}
filter = null;
foreach(var matchingForm in query)
{ ... }
代码崩溃。查询使用filter的当前值,而不是查询时的值。
Jared的版本也好不到哪里去:
var query = from form in forms select form;
var filter = new Filter();
filter.SubFormId = 123;
query = query.Where(c =>
!filter.SubFormId.HasValue || c.SubFormId == filter.SubFormId);
filter = null;
foreach(var matchingForm in query)
{ ... }
这个也崩溃了。
如果过滤器在其他方面发生了变化,那么你的版本和Jared的版本也有不同的语义:
原始代码:var query = from form in forms select form;
var filter = new Filter();
filter.SubFormId = null;
if (filter.SubFormId.HasValue)
{
query = query.Where(c => c.SubFormId == filter.SubFormId);
}
filter.SubFormId = 123;
foreach(var matchingForm in query)
{ ... }
匹配所有形式。你没有添加Where子句
Jared的版本做了一些不同的事情:
var query = from form in forms select form;
var filter = new Filter();
filter.SubFormId = null;
query = query.Where(c =>
!filter.SubFormId.HasValue || c.SubFormId == filter.SubFormId);
filter.SubFormId = 123;
foreach(var matchingForm in query)
{ ... }
只匹配subformId等于123的表单。Jared的版本匹配过滤器子表单Id的当前值(如果有的话),而不管在构建查询时是否有。
记住,查询是表示查询的对象。当您执行查询时,将从头开始重新执行查询,其中的子句将根据查询关闭的变量的当前值进行计算,而不是根据创建查询时存在的变量值的快照。查询有一个实时的、最新的变量视图。
那么假设我也返回这个查询,这允许在方法之外对查询进行计算。由于过滤器将超出作用域,这会导致崩溃吗?
简短回答:
。
较长的回答:
这是这个问题的重复:
Action/Lambda表达式内存管理问题
详情请看我的回答
长答案:
你混淆了作用域和生存期——这是一个常见的错误。作用域纯粹是编译时的概念;局部变量的作用域是程序文本中可以通过变量名引用该变量的区域。变量的生命周期纯粹是一个运行时概念;局部变量的生存期是垃圾收集器知道该存储有效的时间段。
作用域和生命周期经常是相连的;虽然控制在逻辑上位于程序文本的局部范围内的一部分,但已知它是活动的。在查询、lambdas、迭代器块和异步块中使用的局部变量延长了它们的生存期,因此即使控制离开了局部变量的作用域,局部变量仍然存活。本地至少在查询(lambda等)失效之前保持活动状态。
不幸的是,在某些情况下,局部存活的时间可能更长;请看上面的链接问题的例子。有关此问题的详细讨论,请参见这个问题:
c#动作、闭包和垃圾回收
试试下面的
query = query.Where(c =>
!filter.SubFormId.HasValue || c.SubFormId == filter.SubFormId);
您没有指定如果条件不是true
query
的值应该是什么但这可能接近你所期望的
query = query.Where(c =>
filter.SubFormId.HasValue && c.SubFormId == filter.SubFormId);
如果SubFormId
null
query = query.Where(c =>
filter.SubFormId.HasValue? c.SubFormId == filter.SubFormId : true);
导致未过滤的列表
编辑我想补充一下Mark的评论,这太接近
了query = filter.SubFormId.HasValue
? query.Where(c => c.SubFormId == filter.SubFormId)
: query;
基本上就是你的原始代码。您的原始代码将比强制linq样式的版本更有效。