相信大家一定多多少少有碰過當某個資料欄位為 NULL 時,導致過濾出來的資料不正確,而早期的開發人員可能會選擇在資料庫欄位上加上預設值,但因為筆者是使用 Entity Framework 來跟資料庫做溝通,而 EF 在處理預設值時,又會因為每次重匯資料表就要重新設定顯得會有些麻煩,所以下面提供幾種解決方法來跟大家分享。
前言
相信大家一定多多少少有碰過當某個資料欄位為 NULL 時,導致過濾出來的資料不正確,而早期的開發人員可能會選擇在資料庫欄位上加上預設值,但因為筆者是使用 Entity Framework 來跟資料庫做溝通,而 EF 在處理預設值時,又會因為每次重匯資料表就要重新設定顯得會有些麻煩,所以下面提供幾種解決方法來跟大家分享。
問題來源
1.首先我們先看一下我們原本在資料庫的資料
2.接著我們下個條件過濾來模擬這次遇到的問題
神奇的事情就這樣發生了,為什麼 NULL 的資料不會比 AAA 小呢 ?,筆者希望過濾出來的資料像是圖一那樣的結果,如果將這個語法搬回我們的程式碼,大概會像這樣:
var result = db.SL01.where(p=>p.pid.CompareTo("AAA") <= 0).ToList()
當然這邊我們先不管當初設計資料庫的問題,單純以結果來看,第一次碰到這問題也讓筆者百思不得其解,參照微軟的說法是「NULL 並不是零 (數值的或二進位的值)、零長度字串或空白 (字元值) 的同義字。相對的,Null 值可以讓您用來區分零的輸入 (數值的資料行) 或空白 (字元資料行) 以及完全沒有輸入 (NULL 的數值或字元資料行)。」
所以了解真正的知道問題之後,接著就是來想想看如何排除這個問題囉,當然這邊最快的方法將資料庫欄位設成 不允許 NULL,但偏偏因為當時資料庫規劃的不是很好,在加上動了此設定之後會不會在影響到其他程式的結果或是會不會有其他錯誤發生,所以只能重程式這邊將這個問題解掉囉。
解決方法
1.筆者想到的第一個方法是,在建構式中來設定預設值。
[MetadataType(typeof(SL00MD))]
public partial class SL00
{
public SL00()
{
pid2 = String.Empty;
}
}
這個方法看似能解決這個問題,但如果 pid2 的欄位是會顯示在頁面上,當我們 POST 回 Controller 時,一開始 new 出實體時,pid2 的值的確是空字串,但到了 Model Binding 時,因為前端傳進來的值為空時,pid2 欄位自動變成 NULL ,所以這個方法只僅限於當某個欄位不會從頁面前端傳進來時,用建構式來設定預設值的方法才有效。
2.使用 ConvertEmptyStringToNull 屬性
而第一個方法失敗之後,筆者就一直在想為什麼明明前端丟出去的資料顯示是空白,但到了 Model Binding 時卻又自動變成 NULL ,卻有了重大的發現,原來早在 .NET Framework 就有的一個 「ConvertEmptyStringToNull 」屬性,這屬性的功能為「取得或設定值,表示在資料來源中更新資料欄位後,是否將空字串值 ("") 自動轉換為 null 值。」,因為預設的屬性為 true ,代表傳進來的資料若為空值時,預設會從空字串自動轉換為 NULL。
所以我們可以利用 MetadataType 來擴充資料表的驗證屬性:
[MetadataType(typeof(SL00MD))]
public partial class SL00
{
public class SL00MD
{
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string pid2 { get; set; }
}
}
將 ConvertEmptyStringToNull 屬性改為 false 之後,就算再頁面前端沒有輸入最後存近資料庫的資料也會是空字串而不會是 NULL ,當然也就成功解決這個問題了
後記
很多時候魔鬼都藏在細節中,小小的一個屬性設定可以影響很多地方,不過當然別忘了還有其他許多 Model 的屬性可以設定,像是 Required 或是 StringLength ...等等,透過一個簡單的設定可是可以簡化我們許多流程的!
參考連結
新手發文,如有錯誤煩請告知,感謝。
如果喜歡我的文章請按推薦,有任何問題歡迎下面留言~~~
簽名:
學習這條路很廣,喜歡什麼技術不重要,重要的是你肯花時間去學習