從零開始用C#做產品:私人日記(17)內容管理BAL優化

自由踐行 發佈 2021-09-18T17:49:20+00:00

上一節我們開始內容管理的數據設計,這節我們來完成內容管理BAL部分的代碼。在實現Category的時候,增刪改的SQL語句基本上就是一段特定格式的字符串,雖然可以自己做ORM工具來自動生成,但不是我們這個教程要講的。我們今天重點講下對查詢後結果的優化。在Model.

上一節我們開始內容管理的數據設計,這節我們來完成內容管理BAL部分的代碼。

在實現Category的時候,增刪改的SQL語句基本上就是一段特定格式的字符串,雖然可以自己做ORM工具來自動生成,但不是我們這個教程要講的。我們今天重點講下對查詢後結果的優化。

在Model.Categoty類中,我們數據轉換代碼是這樣的:

事實上上面的代碼是不夠安全的,比如:

model.Id = int.Parse(result["Id"].ToString());

result中未必包含Id的鍵值,結果就可能為空,轉換時就會出錯;

result中包含Id的鍵值,但可能不是數字,轉換時也會出錯;

一旦出錯,程序就爆掉了。

正確的代碼應該加上上面兩種情況的判斷,外加調用int.TryParse的方法, 嘗試去轉換,但是這就不是一兩行代碼就能搞定的了,一個欄位的處理可能就要4-5行。如果僅僅是Category還好,欄位不多,Content有12個欄位,還有DateTime這種類型,幾十行代碼來處理這些就顯得過於臃腫了,所以我們需要進行代碼優化。

上面的代碼,共同之處都是從一個Hashtable的實例中獲取數據,不同的地方有兩點:一是返回類型,二是鍵值,有共性就可以用函數,有不同就傳參數,外加C#的一個專門用於工具類的擴展方法,就可以完美解決此問題。

在BAL中我聲明了一個靜態類BALTools,定義了對應Sqlite四種基本類型的函數:

namespace Diary.BAL
{
static public class BALTools
{
static public string toString(this Hashtable ht, string key, string dv = "")
{
if (ht == null) return dv;
if (!ht.Contains(key)) return dv;
if (ht[key] == null) return dv;
return ht[key].ToString();
}

static public int toInt(this Hashtable ht, string key, int dv=0)
{
string str_v = ht.toString(key);
if (string.IsNullOrEmpty(str_v)) return dv;
int.TryParse(str_v, out dv);
return dv;
}

static public DateTime toDateTime(this Hashtable ht, string key)
{
DateTime dv = DateTime.MinValue;
string str_v = ht.toString(key);
if (string.IsNullOrEmpty(str_v)) return dv;
DateTime.TryParse(str_v, out dv);
return dv;
}
static public bool toBool(this Hashtable ht, string key, bool dv = false)
{
string str_v = ht.toString(key);
if (string.IsNullOrEmpty(str_v)) return dv;
return str_v == "1";
}

}
}

這裡有兩個知識點:

第一個知識點擴展方法。大家可能注意到了,每個函數的第一個參數:this+參數類型+參數名。這種在靜態類、靜態函數中,且第一個參數使用了this,編譯器就會為這個指定的參數類型創造出一個擴展方法。相當於你繼承了這個類,可以使用這些方法。

說起來可能比較難理解,看下動圖就很容易理解了:

我們可以看到,一個Hashtable的實例result,直接擁有了toInt、toString等方法,這種就叫擴展方法,設計好了就可以讓你節省大量重複性的代碼。

第二個知識點是在函數參數中加默認值。比如toString的最後一個參數是

string dv = ""

dv就是default value的縮寫,假設我在取值的這個鍵值不存在,那麼我就事先預設一個值,如果不存在,我就用這個預先設置的值來返回。在函數定義的時候設置了一個值是什麼意思呢?就是調用函數的時候我可以不指定這個值,默認使用函數定義的,只有在有必要更改這個默認值的時候,我才需要指定它。

result.toString(「Name」)
result.toString(「Name」, 「無標題」)

都是可以的。

做完上述的改進,我們開始編碼:

Category的查詢部分:

Content的查詢部分:

通過我們的優化,查詢部分代碼的健壯性、可讀性都得到了很大的提升,而且以後再有其他的資料庫我們也都可以復用這幾個方法,這個工作非常值得。我們無論是做項目還是做產品,其實都要不斷地總結、優化,最終形成你自己的代碼庫,設計更加合理,編碼更加高效。其實這也是我極力向大家推薦C#的原因之一,通過你的長期積累,能做的事情就越來越多,用C#是可以全棧開發的,也就是說,等你積累到了一定程度,你基本上可以優雅、高效的做任何你想做的事了,而且自己寫的類庫根本不用擔心版權問題,這是其他很多語言無可比擬的。

我在教程里的內容都沒有用其他的類庫,然後有些人就各種噴,其實我現在寫這個系列教程就跟打網遊的開荒一樣,一切從零開始。噴子們開發只會用輪子,而我們這些獨立開發者是可以自己造輪子的。

DAL.Content的增刪改函數就沒什麼知識點可說的了,大家自行到Git上查閱代碼。

底層工作準備好之後,我們下一節開始界面設計。

----------------------------------------------------

本教程儘量保證2天一更,項目源碼已作為開源項目加入到Gitee,代碼內容會隨教程實時更新,大家有興趣的話可以關注我,以獲得最及時的更新。私信:

私人日記 可以獲取Gitee的連結;

sqlitestudio 可以獲取sqlitestudio的連結;

菜鳥 可以獲取菜鳥教程連結;

大家閱讀過程中有哪些看不懂或未盡興的地方,可以在評論區留言,我會先記下來在後續的教程中找機會再說。

教程有幫助的話請大家幫忙關注、轉發、擴散,能不能開專欄還需要你們的支持!

關鍵字: