【程式開發經驗】EF Core Contains 空白字串陷阱
文章目錄
本文將說明在使用 EF Core Contains 篩選資料時可能遇到的空白字串陷阱,並會解釋如何確認資料庫中的欄位值是否為空白?整理並比較資料庫中的字串資料型態。此外,介紹如何在 SQL Server 取得欄位值的長度。
EF Core Contains 篩選資料
假設有一個產品資料表 Product,包含以下資料:
Name |
---|
紅葉布丁 |
紅葉蛋糕 |
紅磚布丁 |
在 .NET 中,以 Entity Framework Core (EF Core) 寫一段程式碼,篩選特定品項的產品。
db.Products.where(_ => _.Name.Contains(condition));
考慮到 condition
可能包含空白,我們使用 Trim()
方法移除這些空白以正確篩選。
例如,當 condition 為 "布丁 "
,加上 Trim()
方法以 "布丁"
正確取得兩筆資料:「紅葉布丁」、「紅磚布丁」。
db.Products.where(_ => _.Name.Contains(condition.Trim()));
這段程式碼會被 EF Core 自動轉換成以下的 SQL 語法:
SELECT [t].[NAME]
FROM [PRODUCT] AS [t]
WHERE (@condition LIKE N'') OR (CHARINDEX(@condition, [t].[NAME]) > 0)
可以發現 @condition
若為空 ''
,@condition LIKE N''
條件將為 true
,而取得資料表中的所有資料。
因此在進入查詢前,先透過 IsNullOrEmpty()
函式判斷 condition
是否為空;若不為空,才進入查詢。
if(!string.IsNullOrEmpty(condition)) {
db.Products.where(_ => _.Name.Contains(condition.Trim()));
}
然而,當 condition
為多個空白組成的字串 " "
時,因為通過了 if
檢查,所以執行查詢,返回了資料表中的所有資料 :「紅葉布丁」、「紅葉蛋糕」、「紅磚布丁」。
由此可知,由多個空白組成的字串,既不是 NULL
也不是 EMPTY
。
因此,我們也需要在 IsNullOrEmpty()
的檢查中,加上 Trim()
方法移除 condition
中的空白。讓 condition
在只含有多個空白的情況下無法通過檢查。
if(!string.IsNullOrEmpty(condition.Trim())) {
db.Products.where(_ => _.Name.Contains(condition.Trim()));
}
這時可以意識到,保持變數前後一致性的重要。因此,整理一下程式碼。
var queryParam = condition.Trim();
if(!string.IsNullOrEmpty(queryParam)) {
db.Products.where(_ => _.Name.Contains(queryParam));
}
這種由多個空白組成字串的條件查詢,可能發生在人工意外輸入、惡意攻擊;或是資料庫中固定大小的字串資料型態,自動補足空白所致。
看起來是空值,但既不是 NULL 也不是 EMPTY ?
在資料庫中,一個欄位看起來是空值,但實際上可能包含空白字元。
這種誤解情況,特別容易發生在固定大小的字串資料型態 char(n)
或 nchar(n)
中,因為它們會自動補足空白字元。
如何確認資料庫中的欄位值是否為空白?
首先,確認欄位的資料型態是否為 char(n)
或 nchar(n)
。如果是的話,可以透過以下方法確認空白是否為 n 個:
- 將資料匯出至 Excel 檔案,查看欄位值是否由 n 個空白組成。
- 使用
WHERE
篩選資料列,確認欄位值是否為 n 個空白。SELECT * FROM mytable WHERE column = ' ';
- 在 SQL Server 中可使用
DATALENGTH
取得空白數量。SELECT DATALENGTH(column) FROM mytable WHERE id = 1; -- 結果應為 n
為避免需要經常移除字串空白,在設計資料模型前選擇適當的資料型態就顯得相當重要。
比較 CHAR、NCHAR、VARCHAR、NVARCHAR
比較字串資料型態,CHAR
、NCHAR
、VARCHAR
、NVARCHAR
。
特性 | CHAR | NCHAR | VARCHAR | NVARCHAR |
---|---|---|---|---|
描述 | 固定長度的 ASCII 字符型別 | 固定長度的 Unicode 字符型別 | 可變長度的 ASCII 字符型別 | 可變長度的 Unicode 字符型別 |
存儲方式 | 固定長度,右側自動補足空白 | 固定長度,右側自動補足空白 | 根據實際長度存儲,不補空白 | 根據實際長度存儲,不補空白 |
字符編碼 | ASCII | Unicode | ASCII | Unicode |
適用場景 | 固定長度的英數字資料,如手機號碼 | 固定長度的多語言資料,如縣市名 | 可變長度的英數字資料,如護照名 | 可變長度的多語言資料,如中文名 |
長度範圍 | 1 到 8,000 字符 | 1 到 4,000 字符 | 1 到 8,000 字符 | 1 到 4,000 字符 |
示例 | CHAR(10) | NCHAR(10) | VARCHAR(50) | NVARCHAR(50) |
是否支持多語言 | 否 | 是 | 否 | 是 |
SQL Server 取得欄位值長度
在 SQL Server 中使用 LEN
或 DATALENGTH
函數,取得欄位值長度。以下提供範例,說明兩者的不同之處。
LEN
LEN 函數用來計算字串中字符的數量,但不計算尾端的空白。
SELECT LEN('Hello World') -- 結果是 11
SELECT LEN('Hello World ') -- 結果也是 11,尾端空白不計算
SELECT LEN('你好') -- 結果是 2
SELECT LEN('') -- 結果是 0
SELECT LEN(NULL) -- 結果是 NULL
DATALENGTH
DATALENGTH 函數用來計算字串的總長度,包含所有的字符和空白。 但無法正確計算 Unicode 編碼的字元數量。
SELECT DATALENGTH('Hello World') -- 結果是 11
SELECT DATALENGTH('Hello World ') -- 結果是 14,包含尾端的3個空白
SELECT DATALENGTH('你好') -- 結果是 4,無法正確計算 Unicode 編碼的字元數量
SELECT DATALENGTH('') -- 結果是 0
SELECT DATALENGTH(NULL) -- 結果是 NULL
經驗總結
- 由多個空白組成的字串,既不是
NULL
也不是EMPTY
。 - 比較字串值之前,習慣先移除空白。
- 設計資料模型時,適當的資料型態,可避免不必要的空白問題。