我有一个来自MVC提交的viewmodel类,我希望根据用户填写的值从EF6获取结果集,但忽略模型中那些为null的项:
public class SearchFilterVM
{
public int? ID { get; set; }
public bool? Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleInitial { get; set; }
public DateTime? DateOfBirth { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
}
基本上我正在寻找的是像这样的伪代码:
var results = context.Members
.Where(x => x.Active == vm.Active.Value) // but only if vm prop is not null
.Where(x => x.FirstName == vm.FirstName) // but only if vm prop is not null
.Where(x => x.LastName == vm.LastName) // but only if vm prop is not null
.ToList();
(例如,如果过滤器模型属性为null,则完全忽略它们)
当然我不想使用类似上面的内容,因为它会根据第一个标准进行查询,然后根据第二个适用标准等进行重新查询,直到完成(我正在使用500k +行)。< / p>
我想不出为此使用LINQ查询表达式的方法。
我能做的是根据值的存在构建参数化的SQL语句并附加条件,最后通过EF6的RawSQLQuery传递它,这将在性能方面有效(并且可以很好地控制索引字段的顺序)为了更好的调整),但我想知道是否有一种“自然”方式通过LINQ完成同样的事情。
答案 0 :(得分:5)
我建议的方法
请记住,EF使用延迟执行,并且在您使用ToList()
实现任何查询或者例如迭代它之前,您实际上并不执行任何查询。这意味着你可以这样做:
var results = context.Members;
if(vm.Active.HasValue)
{
results = results.Where(x => x.Active == vm.Active.Value);
}
if(!string.IsNullOrEmpty vm.FirstName))
{
results = results.Where(x => x.FirstName == vm.FirstName);
}
//and so on until...
return results.ToList();
其他方法
我以为我会额外添加这个额外的免费赠品,以了解为什么你可能不想使用其他答案中提到的技术。让我们说你这样做了:
string name = "bob";
var users = context.Users.Where(u => name == null || u.Name == name).ToList();
这看起来与我的版本非常相似,并且会给出相同的结果,但SQL查询却完全不同。你最终会得到这样的东西:
DECLARE @p__linq__0 NVarChar(1000) = 'bob'
DECLARE @p__linq__1 NVarChar(1000) = 'bob'
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
/* snip */
FROM [dbo].[Users] AS [Extent1]
WHERE @p__linq__0 IS NULL OR [Extent1].[Name] = @p__linq__1
请注意,现在在数据库中完成了空检查,并且您还发送了两次参数。好吧,它可能不会明显变慢,但这可能是你未来想要记住的事情。
答案 1 :(得分:3)
您可以在where语句中检查VM变量是否为NULL:
var results = context.Members
.Where(x => vm.Active == null || x.Active == vm.Active.Value) // but only if it's not null
.Where(x => vm.FirstName == null || x.FirstName == vm.FirstName) // but only if it's not null
.Where(x => vm.LastName == null || x.LastName == vm.LastName) // but only if it's not null
.ToList();
在这种情况下,例如,如果vm.FirstName
为null
,则第一个语句vm.FirstName == null
将为真(如果不为空,则实际上将检查名字是否相等等等其他条件。
答案 2 :(得分:0)
以下将过滤器组合到1 where子句中,但是当开始枚举结果时,过滤器返回的IQueryable将被构建(并执行),因此不需要太多(例如,通过调用ToList())。
var results = context.Members
.Where(x =>
(!vm.Active.HasValue() || x.Active == vm.Active.Value)
&& (vm.FirstName == null || x.FirstName == vm.FirstName)
&& (vm.LastName == null || x.LastName == vm.LastName))
.ToList();