当期望使用隐式类型声明或重构已经具有隐式类型的代码 (var) 时,这是一个常见的陷阱。 current accepted answer 非常有效,但将所有表达式变体减少为单个 1-liner linq 表达式很容易影响代码的可读性。
在这种情况下,由于在查询结束时投影到匿名类型而使问题变得复杂,这可以通过对投影使用显式类型定义来解决,但是将查询构造分解为多个更简单步骤:
var customerQuery = _context.Customers().AsQueryable();
if (Type == 1)
customerQuery = customerQuery.Where(t => t.type == "current");
else
customerQuery = customerQuery.Where(...);
... // any other filtering or sorting expressions?
var lstCurrent = customQuery.Select(c => new { c.LastName, c.City});
return View(lstCurrent.ToList());
当然,最后一段可能是单行,或者如果没有进一步引用lstCurrent,编译器可能会将其优化为以下内容:
return View(customQuery.Select(c => new { c.LastName, c.City}).ToList());
在此示例中,我特意转换为 IQueryable<T>,以确保此解决方案与 IQueryable<T>/DbSet<T> 上下文和存储库样式 IEnumerable<T> 上下文兼容。
这种变化通常是最先想到的,但是我们仍然在两个地方声明了查询的来源,这增加了这段代码的歧义以及在以后的重构中出现分歧的风险(不小心只编辑了一个分支并且不维护另一个分支中的代码):
IQueryable<Customer> customerQuery = null;
if (Type == 1)
customerQuery = _context.Customers().Where(t => t.type == "current");
else
customerQuery = _context.Customers().Where(...);
... // any other filtering or sorting expressions?
var lstCurrent = customQuery.Select(c => new { c.LastName, c.City});
return View(lstCurrent.ToList());
另一种解决方案是将输出明确定义为其自己的具体类:
public class CustomerSummary
{
public string LastName { get;set; }
public string City { get;set; }
}
...
List<Customers> customers = null;
if (Type == 1)
customers = _context.Customers().Where(c => c.type == "current")
.Select(c => new CustomerSummary
{
LastName = c.LastName,
City = c.City
}).ToList();
else
customers = _context.Customers().Where(c => ...)
.Select(c => new CustomerSummary
{
LastName = c.LastName,
City = c.City
}).ToList();
... // any other filtering or sorting expressions?
return View(customers);
一次性的代码很多,但如果你让它足够抽象,它可以在其他场景中重复使用,我仍然会将它与第一个代码示例结合起来,它保留了源、过滤器和投影逻辑分离,在应用程序的整个生命周期中,这 3 个元素往往会发生不同的演变,因此分离出代码使得重构或未来维护更容易完成和审查。