仅当condition不为null时才通过Where子句进行过滤

Ste*_*ven 3 c# linq asp.net

我有这个控制器方法:

public ActionResult Index(string searchError)
{
    // get all errors
    var viewModel = _errorsRepository.Errors.OrderByDescending(e => e.TimeUtc).
                            Select(e => new ErrorViewModel
                                            {
                                                ErrorId = e.ErrorId,
                                                Message = e.Message,
                                                TimeUtc = e.TimeUtc
                                            });

    if (!String.IsNullOrEmpty(searchError))
            viewModel = viewModel.Where(e => e.Message.ToLower().Contains(searchError.Trim().ToLower()));

    return View(viewModel);
}
Run Code Online (Sandbox Code Playgroud)

我认为做额外的过滤器会减慢一切,我想知道是否可以将Where子句添加到Select语句并检查是否searchError为null内联.

这可能吗?

Bro*_*ass 7

由于Linq是懒惰的,如果你有"一个大的陈述"或多个,只要你不执行你的查询(例如通过迭代结果或强制执行急切执行ToList())并不重要,因为你只是链接扩展方法.在这方面,我将重点放在可读性上.

有些事情需要考虑,例如排序不能懒惰(你必须先查看所有项目才能按顺序吐出物品) - 这就是为什么你应该总是把Where过滤器放在你的前面,OrderBy这样你就可以减少排序的项目.这说我会像这样重构你的代码:

// get all errors
var viewModel = _errorsRepository.Errors;

// optionally filter            
if (!String.IsNullOrEmpty(searchError))
{
    string searchErrorMatch = searchError.Trim().ToLower();
    viewModel = viewModel.Where(e => e.Message.ToLower().Contains(searchErrorMatch));
}

//order and project to ErrorViewModel
viewModel = viewModel.OrderByDescending(e => e.TimeUtc)
                     .Select(e => new ErrorViewModel
                      {
                          ErrorId = e.ErrorId,
                          Message = e.Message,
                          TimeUtc = e.TimeUtc
                      }).ToList();
Run Code Online (Sandbox Code Playgroud)

另请注意,我从searchError.Trim().ToLower()lambda中退出并将其分配给变量一次 - 否则每次评估lambda 都会执行此操作,这实际上是不必要的工作.

最后的编辑:我还添加了一个ToList()在你的投影后执行查询 - 否则你的查询真的会从你的视图中执行,这通常很糟糕,原因很多,例如你必须让数据库上下文保持活动可能更长的时间和您违反了关注点 - 视图应仅关注视图模型,而与获取数据无关.