我有一个具有Nullable ParentId字段的Category实体。当下面的方法正在执行且categoryId为null时,结果似乎为null,但是有些类别具有null ParentId值。
这里有什么问题,我错过了什么?
public IEnumerable<ICategory> GetSubCategories(long? categoryId)
{
var subCategories = this.Repository.Categories.Where(c => c.ParentId == categoryId)
.ToList().Cast<ICategory>();
return subCategories;
}
顺便说一句,当我将条件更改为(c.ParentId == null)时,结果似乎正常。
答案 0 :(得分:53)
其他方式:
Where object.Equals(c.ParentId, categoryId)
或
Where (categoryId == null ? c.ParentId == null : c.ParentId == categoryId)
答案 1 :(得分:29)
要做的第一件事就是进行日志记录,以查看生成的TSQL;例如:
ctx.Log = Console.Out;
LINQ-to-SQL似乎对空值有点不一致(取决于文字与值):
using(var ctx = new DataClasses2DataContext())
{
ctx.Log = Console.Out;
int? mgr = (int?)null; // redundant int? for comparison...
// 23 rows:
var bosses1 = ctx.Employees.Where(x => x.ReportsTo == (int?)null).ToList();
// 0 rows:
var bosses2 = ctx.Employees.Where(x => x.ReportsTo == mgr).ToList();
}
所以我可以建议使用带有null的顶部表单!
即
Expression<Func<Category,bool>> predicate;
if(categoryId == null) {
predicate = c=>c.ParentId == null;
} else {
predicate = c=>c.ParentId == categoryId;
}
var subCategories = this.Repository.Categories
.Where(predicate).ToList().Cast<ICategory>();
更新 - 我使用自定义Expression
static void Main()
{
ShowEmps(29); // 4 rows
ShowEmps(null); // 23 rows
}
static void ShowEmps(int? manager)
{
using (var ctx = new DataClasses2DataContext())
{
ctx.Log = Console.Out;
var emps = ctx.Employees.Where(x => x.ReportsTo, manager).ToList();
Console.WriteLine(emps.Count);
}
}
static IQueryable<T> Where<T, TValue>(
this IQueryable<T> source,
Expression<Func<T, TValue?>> selector,
TValue? value) where TValue : struct
{
var param = Expression.Parameter(typeof (T), "x");
var member = Expression.Invoke(selector, param);
var body = Expression.Equal(
member, Expression.Constant(value, typeof (TValue?)));
var lambda = Expression.Lambda<Func<T,bool>>(body, param);
return source.Where(lambda);
}
答案 2 :(得分:5)
我的猜测是,这是由于DBMS的一个相当常见的属性 - 仅仅因为两个都是空的并不意味着它们是相等的。
详细说明一下,尝试执行这两个查询:
SELECT * FROM TABLE WHERE field = NULL
SELECT * FROM TABLE WHERE field IS NULL
“IS NULL”构造的原因是在DBMS世界中,NULL!= NULL,因为NULL的含义是该值未定义。由于NULL意味着未定义,因此您不能说两个空值相等,因为根据定义您不知道它们是什么。
当您明确检查“field == NULL”时,LINQ可能会将其转换为“field IS NULL”。但是当你使用变量时,我猜测LINQ不会自动进行转换。
以下是an MSDN forum post,其中包含有关此问题的详细信息。
看起来好的“作弊”是将你的lambda改成这样:
c => c.ParentId.Equals(categoryId)
答案 3 :(得分:5)
您需要使用运算符Equals:
var subCategories = this.Repository.Categories.Where(c => c.ParentId.Equals(categoryId))
.ToList().Cast<ICategory>();
等于可以为空的类型返回 true 如果:
并在以下情况下返回 false :
此处有更多信息Nullable<.T>.Equals Method
答案 4 :(得分:1)
这样的事情怎么样?
public IEnumerable<ICategory> GetSubCategories(long? categoryId)
{
var subCategories = this.Repository.Categories.Where(c => (!categoryId.HasValue && c.ParentId == null) || c.ParentId == categoryId)
.ToList().Cast<ICategory>();
return subCategories;
}
答案 5 :(得分:1)
或者你可以简单地使用它。它还将转换为更好的SQL查询
Where((!categoryId.hasValue && !c.ParentId.HasValue) || c.ParentId == categoryId)
答案 6 :(得分:0)
Linq to Entities支持Null Coelescing(??),因此只需将空值即时转换为默认值。
Where(c => c.ParentId == categoryId ?? 0)