Nullable ParentIdフィールドを持つCategoryエンティティがあります。以下のメソッドが実行され、categoryIdがnullの場合、結果はnullのように見えますが、ParentId値がnullのカテゴリがあります。
ここの問題は何ですか、何が欠けていますか?
public IEnumerable<ICategory> GetSubCategories(long? categoryId)
{
var subCategories = this.Repository.Categories.Where(c => c.ParentId == categoryId)
.ToList().Cast<ICategory>();
return subCategories;
}
ところで、条件を(c.ParentId == null)に変更すると、結果は正常に見えます。
最初に行うことは、TSQLが生成されたものを確認するためにログを記録することです。例えば:
ctx.Log = Console.Out;
LINQ-to-SQLは、nullを少し矛盾して処理するようです(リテラルと値に依存):
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();
}
したがって、私が提案できるのは、ヌルを含むトップフォームを使用することだけです!
つまり.
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);
}
他の方法:
Where object.Equals(c.ParentId, categoryId)
または
Where (categoryId == null ? c.ParentId == null : c.ParentId == categoryId)
演算子Equalsを使用する必要があります。
var subCategories = this.Repository.Categories.Where(c => c.ParentId.Equals(categoryId))
.ToList().Cast<ICategory>();
Equalsfot nullable types戻り値true次の場合:
次の場合、falseを返します。
詳細はこちら Nullable <.T> .Equals Method
私の推測では、それはDBMSのかなり一般的な属性によるものである-2つのものが両方ともnullであるからといって、それらが等しいというわけではありません。
少し詳しく説明するには、次の2つのクエリを実行してください。
SELECT * FROM TABLE WHERE field = NULL
SELECT * FROM TABLE WHERE field IS NULL
"IS NULL"構造の理由は、DBMSの世界ではNULL!= NULLであるためです。NULLの意味は、値が未定義であることです。 NULLは未定義を意味するため、2つのNULL値が等しいと言うことはできません。定義により、それらが何であるかがわからないからです。
「field == NULL」を明示的にチェックすると、おそらくLINQはそれを「field IS NULL」に変換します。しかし、変数を使用すると、LINQは自動的に実行しないと推測しますその変換。
MSDNフォーラムの投稿 は、この問題に関する詳細情報です。
良い「チート」のように見えますが、ラムダを次のように変更することです。
c => c.ParentId.Equals(categoryId)
または、単純にこれを使用できます。また、より良いSQLクエリに変換されます
Where((!categoryId.hasValue && !c.ParentId.HasValue) || c.ParentId == categoryId)
このような単純なものはどうですか?
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;
}
Linq to EntitiesはNull Coelescing(??)をサポートしているため、その場でnullをデフォルト値に変換するだけです。
Where(c => c.ParentId == categoryId ?? 0)