【问题标题】:Ternary Operator in LINQ Where clause for nullable bool column可空布尔列的 LINQ Where 子句中的三元运算符
【发布时间】:2015-10-13 14:03:35
【问题描述】:

任何人都可以在此 linq 语句的 where 中看到三元组有什么问题吗:

var organizations = Context.Set<Domain.Content.Organisation>()
                    .Where(x => x.ShowCompanyPage == (showCompanyPagesOnly ? true : x.ShowCompanyPage))

如果 showCompanyPagesOnly 设置为 true,我会得到 4 个结果,这是正确的,只有四个公司的 ShowCompanyPage = true。

但是,如果我将其设置为 false,我预计会有 1000 多个结果(所有公司)。但我仍然只得到 4 个。

我的逻辑是不是:

if showCompanyPagesOnly is true, then give me results where  x.ShowCompanyPage == true

else give me results where  x.ShowCompanyPage = whatever is in the column (ie ALL Organisations)

?

x.ShowCompanyPage 是一个可为空的 bool 列。

完整代码:

public Result<IList<Organisation>> GetAllOrganisations(bool showCompanyPagesOnly = false)
    {
        var result = new Result<IList<Organisation>>();

        try
        {
            var organizations = Context.Set<Domain.Content.Organisation>()
                .Where(x => x.ShowCompanyPage == (showCompanyPagesOnly == true ? true : x.ShowCompanyPage)) // show only company pages or show all
                .AsNoTracking()
                .Select(x => new DataContracts.Content.Organisation
                {
                    Id = x.Id,
                    Name = x.Name,
                    OrganisationTypeId = x.OrganisationTypeId,
                    IsCustomer = x.IsCustomer,
                    SeoName = x.SeoName,
                    Description = x.Description,
                    Website = x.Website
                }).OrderBy(x => x.Name).ToList();

            result.Data = organizations;
        }
        catch (Exception ex)
        {
            result.SetException(ex);
            HandleError(ex);
        }
        return result;

    }

【问题讨论】:

  • 似乎毫无意义,因为 x.ShowCompanyPage == x.ShowCompanyPage 将始终为真,所以您真正拥有的是 x.ShowCompanyPage == showCompanyPagesOnly
  • @MikeT 我不认为你是正确的,如果 showCompanyPagesOnly 我想一次显示所有。列中的值可以是 true、false 或 null。但是您的建议一次限制为一个值。
  • 错过阅读你真正拥有的是(showCompanyPagesOnly ? true : true )
  • 如果 showCompanyPagesOnly 为真,我会检查我是否在关注您想要的内容,您想要 ShowCompanyPage = true 的记录,否则您想要所有内容?
  • @MikeT - 正是 - 这就是我想要的逻辑。有什么想法吗?

标签: c# linq where-clause ternary-operator


【解决方案1】:

有时当逻辑变得过于复杂时,最好的答案就是把问题颠倒过来,目前你在问

如果 showCompanyPagesOnly 为真,我如何仅获取带有 ShowCompanyPage = true 的那些

如果您将其与获取所有内容交换,除非 showCompanyPagesOnly 为真并且您的逻辑变为简单的 OR 语句

showCompanyPagesOnly 不为真或 ShowCompanyPage 为真 这是

x => (!showCompanyPagesOnly) || x.ShowCompanyPage

你可能需要这样做

   x => (!showCompanyPagesOnly) || (x.ShowCompanyPage ?? false)/*default value depends on if you want to treat null as true or false*/)

考虑可空性

【讨论】:

  • 为了清楚起见,我实现了这个,它是正确的:x => (!showCompanyPagesOnly) || (x.ShowCompanyPage ?? false)
  • 所以我们知道为什么这有效,但您的原始逻辑没有?当不涉及实体框架时,两者都可以工作。
  • @MobyDisk - 不幸的是我不知道。我也很惊讶我的原始代码不起作用。我现在没有时间调试,因为它是一个工作项目,但我可能会回来。
【解决方案2】:

这是一种更好的方法,因为它将生成两个不同的 LINQ 查询,这将允许 SQL Server 生成两个不同的查询计划,这在大多数情况下会极大地影响查询的性能:

public Result<IList<Organisation>> GetAllOrganisations(bool showCompanyPagesOnly = false)
{
    var result = new Result<IList<Organisation>>();

    try
    {
        var organizations = Context.Set<Domain.Content.Organisation>()
            .AsNoTracking();

        if (showCompanyPagesOnly)
            organizations=organization
            .Where(x => x.ShowCompanyPage == true);

        result.Data = organizations
            .Select(x => new DataContracts.Content.Organisation
            {
                Id = x.Id,
                Name = x.Name,
                OrganisationTypeId = x.OrganisationTypeId,
                IsCustomer = x.IsCustomer,
                SeoName = x.SeoName,
                Description = x.Description,
                Website = x.Website
            }).OrderBy(x => x.Name).ToList();
    }
    catch (Exception ex)
    {
        result.SetException(ex);
        HandleError(ex);
    }
    return result;

}

【讨论】:

  • +1 这正是我会做的。但是罗伯特,出于好奇,我想知道这是否真的需要 - 如果使用其他(典型)答案中的简单条件,Linq 提供程序不应该消除常量表达式路径并生成不同的 sql 请求吗?
  • @robert-mckee 感谢您提供此解决方案。你能解释一下为什么在这种情况下生成两个查询更好吗?我可能缺乏理解,这让我想知道两次访问 DB 是不是很糟糕?
  • @robert-mckee 好吧,我开始意识到您不会两次访问数据库。每个方法调用只命中一次,但根据 showCompanyPagesOnly 是真还是假会生成两个不同的查询,因此具有执行路径优势。
  • @IvanStoev Linq 提供程序并不那么聪明。它将参数化常量,并将其传递给 SQL Server,然后 SQL Server 将使用参数嗅探来生成查询计划。不幸的是,即使参数发生变化,SQL Server 也会重新使用相同的查询计划,从而导致一些非常不理想的性能。谷歌“SQL Server 参数嗅探”了解更多详情。细节太详细了,不适合评论(甚至是答案)。
  • @fourbeatcoder 正确,每次调用它仍然只命中数据库一次,但查询不同,这允许 SQL Server 有两个单独的查询计划与之关联(一个查询 ..==true,另一个没有where子句)
【解决方案3】:

试试这个:

.Where(x => showCompanyPagesOnly ? x.ShowCompanyPage == true : true)

这是fiddle

Where() 函数返回满足条件的记录,因此条件必须计算为布尔值(即truefalse)。如果您将值 true 放在条件中,那么您实际上是在要求 Where() 函数返回所有记录。它类似于:

if(true){
    //do something.
}

如您所知,这将始终执行“做某事”。

【讨论】:

  • 不幸的是,您的建议会导致构建错误:无法隐式转换类型 'bool?' '布尔'。存在显式转换(您是否缺少演员表?)
  • @stuartd 您建议的解决方案会导致错误:LINQ to Entities 无法识别方法 'Boolean GetValueOrDefault()' 方法,并且此方法无法转换为存储表达式。
  • @RacilHilan 我认为您编辑的解决方案不正确,如果 showCompanyPagesOnly 为假,我想立即显示所有内容。列中的值可以是 true、false 或 null。但是您的建议一次限制为一个值。
  • 不,您对条件的理解不正确。这就是确切的含义:如果showCompanyPagesOnlytrue,则返回将x.ShowCompanyPage 设置为true 的公司。如果showCompanyPagesOnly 为假,则返回所有内容。我会在我的答案中添加一个解释和一个小提琴。
  • @RacilHilan - 谢谢你的解释。您是正确的,您编辑的解决方案有效。然而,mikeT 在你 3 分钟前回答正确,我接受了这个答案。我对你的投了赞成票。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-10
  • 2019-06-26
  • 1970-01-01
  • 2019-04-08
  • 2021-09-06
  • 2021-07-14
  • 2011-01-10
相关资源
最近更新 更多