jle*_*ach 4 c# linq entity-framework entity-framework-6
我有一个来自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; }
}
Run Code Online (Sandbox Code Playgroud)
基本上我正在寻找的是像这样的伪代码:
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();
Run Code Online (Sandbox Code Playgroud)
(例如,如果过滤器模型属性为null,则完全忽略它们)
当然我不想使用类似上面的内容,因为它会根据第一个标准进行查询,然后根据第二个适用的标准等进行重新查询,直到完成(我正在使用500k +行).
我想不出为此使用LINQ查询表达式的方法.
我能做的是基于值的存在构建一个参数化的SQL语句并附加条件,最后通过EF6的RawSQLQuery传递它,这将在性能方面起作用(并且让我很好地控制索引字段的顺序以便更好地调整),但我想知道是否有一种"自然"方式通过LINQ完成同样的事情.
我的建议方法
请记住,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();
Run Code Online (Sandbox Code Playgroud)
其他方法
我以为我会额外添加这个额外的免费赠品,以了解为什么你可能不想使用其他答案中提到的技术.让我们说你这样做了:
string name = "bob";
var users = context.Users.Where(u => name == null || u.Name == name).ToList();
Run Code Online (Sandbox Code Playgroud)
这看起来与我的版本非常相似,并且会给出相同的结果,但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
Run Code Online (Sandbox Code Playgroud)
请注意,现在在数据库中完成了空检查,并且您还发送了两次参数.好吧,它可能不会明显变慢,但这可能是你未来想要记住的事情.
小智 5
您可以在 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();
Run Code Online (Sandbox Code Playgroud)
在这种情况下,例如,如果vm.FirstName将是null,则第一个语句vm.FirstName == null将为真(如果不为空,则它实际上会检查名字是否相等)等其他条件。