条件为true且可空对象必须具有值

3

我一直在寻找答案,但是没有找到任何有用的帮助。我遇到了这个错误:

可空对象必须具有值。

我的请求是:

from e in dc.tblElements 
where
    e.IsUnique &&
    (e.TypeID == 2) &&     
    (categoryId != null ? e.CategoryId.Value == categoryId.Value : true) &&
    ((e.Name.Contains(keyword)) ||
    (e.Keywords.Contains(keyword)))
select e

where条件中的第三行是问题所在(categoryId)。如果categoryId有值,它可以工作,但当它为null时却不能。然而,我用true替换了这一行,它也能工作。我不明白问题出在哪里。

在我的表中,CategoryId可以为null,因此我尝试:

(categoryId.HasValue && e.CategoryId.HasValue ? e.CategoryId.Value == categoryId.Value : true) 

我想做的是:根据where条件选择此表的所有元素。categoryId来自下拉菜单,因此如果在用户发出请求时仍选择默认值,则无论类别如何,我都要显示所有元素。


你不需要那行代码。在LINQ中没有必要使用“catch-all”参数,你可以根据需要添加另一个Where()子句。 - Panagiotis Kanavos
你能用通俗易懂的语言描述一下你想要什么吗? - nvoigt
我编辑了这个主题。@Panagiotis Kanavos,你能详细解释一下你的想法吗? - Tyler Durden
4个回答

5
您只需比较两个变量即可:
e.CategoryId == categoryId

如果您希望对一个值为 NULL 的特殊处理,可能是因为您希望它成为一个特殊情况,在这种情况下 NULL 能够匹配 所有 数据而不仅仅只是其他 NULL 值,那么您可以添加如下代码:
e.CategoryId == categoryId || !e.CategoryId.HasValue || !categoryId.HasValue

您的问题在于访问了 .Value。如果您在内存中使用 Linq-To-Objects 运行代码,那么它会工作,因为编译器只会运行 if 语句(三元运算符,我知道你理解我的意思)。但是对于数据库来说,需要准备一个完整的语句。该语句必须完整存在,不使用任何短路计算。因此,语句构建器将访问您的两个分支来构建该语句以供数据库使用,其中一个分支将失败,因为它访问 .Value,尽管不存在。

在你的情况下(第二行),如果categoryId = 5,我将获取所有categoryId = 5的元素,但也会获取没有分类的元素。当categoryId有值时,我只想要这个categoryId的元素。我是对的吗? - Tyler Durden
1
@TylerDurden 我不能告诉你你想要什么。这就是为什么我在你的问题评论中问了。如果你能描述你想要发生什么,我们可以帮助你实现。 - nvoigt
如果这是 SQL,那么它将成为万能参数反模式。如果 categoryId 为空,则正确的选项是根本不添加条件。 - Panagiotis Kanavos
@nvoigt 好的,我想我明白你的意思了。谢谢你的回复和解释。我通过 ((categoryId.HasValue) ? (p.CategoryID == categoryId) : true) 改变了这一行,现在它运行得非常好。 - Tyler Durden
@TylerDurden 最好先检查性能。查询优化器将缓存用于第一次执行的计划。如果第一次执行具有空的CategoryID参数,则可能不会使用包括categoryId的任何索引。这可能会对具有非空CategoryID参数的调用产生性能影响,例如在包含CategoryID的索引上执行表扫描而不是搜索。 - Panagiotis Kanavos

1
将CategoryId设置为可空类型并尝试。
Nullable<int> CategoryId = null;

1

看起来你正在尝试实现一个“catch-all”categoryId参数。这是SQL中的反模式,也是一种可能导致性能不佳的强烈警示。

在LINQ中,这是不必要的,因为你可以通过向查询中添加另一个.Where()调用来添加.Where()条件,例如:

var query = from e in dc.tblElements 
            where
                e.IsUnique &&
                e.TypeID == 2 &&     
                ( e.Name.Contains(keyword) ||
                  e.Keywords.Contains(keyword) )
            select e;
if (categoryId.HasValue)
{
    query=query.Where(e.CategoryId == categoryId);
}

您可以在运行时使用此功能添加多个条件。

1

试试这个:

from e in dc.tblElements 
where
    e.IsUnique &&
    (e.TypeID == 2) &&     
    (categoryId.HasValue && e.CategoryId.Value == categoryId.Value) &&
    ((e.Name.Contains(keyword)) ||
    (e.Keywords.Contains(keyword)))
select e

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接