EntityFramework 在比較 null 跟 int 的差異性

如果說今天有一個欄位在資料庫中被設計成可為 null 的 int ,在使用 Linq 語法上會變得比較麻煩。

  • EntityFramework 4.3.1

資料庫的設計

dbo.category

Id Name ParentId
1 Computer NULL
2 TV NULL
3 Laptop 1
4 Desktop 1

在這邊的需求是當參數 ParentId 傳進來的的時候,要能夠找到對應的資料。

利用 == 比較子

public ViewResult Index(int? parentId)
{
var categories = db.Categories
.Where(it => it.ParentId == parentId);
return View(categories);
}

在這邊比較的時候會產生一個問題,EntityFramework 在解析 Linq 語法成為 T-SQl 的時候會將 == 解析為 = ,所以當傳入的 parentId 為 null 的時候,這邊比較的語法會是

WHERE ParentId = NULL

null 的比較這樣是錯誤的,要找到 null 的資料應該是利用 is null 來當條件。

  • 傳入資料是 null > 沒有找到 ParentId = null 資料
  • 傳入資料不是 null > 可以正確找到資料

利用 Equals 比較子

public ViewResult Index(int? parentId)
{
var categories = db.Categories
.Where(it => it.ParentId.Equals(parentId));
return View(categories);
}

如果是傳入為 null 的時候也要能比較出 null 的資料,這時候用 Equals 當作運算子,則在解析成為 T-SQL 的時候成為

WHERE ParentId IS NULL

這樣就可以比較 null 的資料了,但是問題並非這麼單純,因為當傳入參數不為 null 的時候,會產生一個錯誤

Unable to create a constant value of type 'System.Object'. Only primitive 
types or enumeration types are supported in this context.

  • 傳入資料是 null > 可以正確找到資料
  • 傳入資料不是 null > 產生 Unable to create a constant value of type ‘System.Object’ Exception

整合兩邊的方法

在這個情況之下這個查詢勢必需要先判斷再送進 SQL 查詢的動作了

public ViewResult Index(int? parentId)
{
if (parentId.HasValue)
{
var categories = db.Categories
.Where(it => it.ParentId == parentId)
.ToList();
}
else
{
var categories = db.Categories
.Where(it => it.ParentId.Equals(parentId))
.ToList();
}
return View(categories);
}

先判斷傳入參數是否為 null 再根據不同情況做查詢

另一種選擇的解決方法

或著是不想要寫兩段 Linq 的也可以考慮

public ViewResult Index(int? parentId = 0)
{
var categories = db.Categories
.Where(it => (it.ParentId.HasValue ? it.ParentId : 0) == parentId)
.ToList();

return View(categories);
}

在這邊的三元替換轉換成 T-SQL 會是

WHERE (CASE WHEN (ParentId IS NOT NULL) THEN ParentId ELSE 0 END) = @p__linq__0


differences between int32.Equals int? == int? for linq

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *