【问题标题】:EF6 Query Criteria using object properties that aren't null使用不为空的对象属性的 EF6 查询条件
【发布时间】:2016-10-11 14:39:25
【问题描述】:

我有一个来自 MVC 提交的视图模型类,我想根据用户填写的值从 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();

(例如,如果过滤器模型属性为空,则完全忽略它们)

我自然不想使用类似上面的东西,因为它会根据第一个条件进行查询,然后根据第二个适用条件重新查询,等等,直到完成(我正在处理 500k+ 行)。

我也想不出为此使用 LINQ 查询表达式的方法。

我可以做的是根据值的存在构建一个参数化的 SQL 语句并附加条件,最后通过 EF6 的 RawSQLQuery 传递它,这将在性能方面发挥作用(并让我很好地控制索引字段的顺序为了更好地调整),但我想知道是否有一种“自然”的方式可以通过 LINQ 完成同样的事情。

【问题讨论】:

    标签: c# entity-framework linq entity-framework-6


    【解决方案1】:

    我的建议方法

    请记住,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
    

    请注意,现在在数据库中完成了空值检查,并且您还两次发送了参数。好的,它可能不会明显变慢,但您可能需要记住这一点,以备将来使用。

    【讨论】:

    • 那怎么样!这正是我认为不会起作用的(我不知道延迟执行)。非常感谢
    • 这是IQueryable 接口非常有用的特性之一(在 EF 的上下文中),您正在构建一个查询(在很大程度上与您在 olden 中构建一个 SQL 字符串的方式相同)天)
    • 只是一个快速修复,var results = context.MyEntities 需要专门声明为IQueryable<Domain.MyEntity> results = context.MyEntities 以进行适当的向下转换,否则结果类型为 DbSet 和应用 Where 结果的后续应用程序失败。
    • 是的,你也可以context.MyEntities.AsQueryable()
    【解决方案2】:

    您可以在 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 将为真(如果不为空,则它实际上会检查名字是否相等)等等条件。

    【讨论】:

    • 好的,很酷,不知道我可以在 Where 表达式中使用该语法。我会投票,但会给 DavidG 的答案。谢谢!
    • 此方法的缺点是您将空检查发送到数据库。
    猜你喜欢
    • 2010-12-04
    • 2014-08-16
    • 2021-05-16
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多