所做的小項目中需要多級分類, 試著學習實現多級分類。由於對預存程序本身並不大熟悉,也不想藉助於treeview,於是遞迴邏輯採用C#實現,配合數資料庫完成了多級分類的擷取方法。增加分類節點應該說是比較簡單的,此文暫略。
資料庫表:CategoryInfo
欄位名 類型
ciID int //記錄序號,自增量
ciName nvarchar(20) //分類名
ciParent int //父分類序號
ciLayer int //所處的層次
ciDescription nvarchar(200) //對分類的描述
擷取子分類的預存程序
CREATE PROCEDURE [dbo].[category_getChild]
@cName nvarchar(20)
AS
BEGIN
DECLARE @tmpID int
SELECT @tmpID=ciID FROM CategoryInfo
WHERE RTRIM(ciName) = RTRIM(@cName)
if(@tmpID IS NOT NULL)
SELECT * FROM CategoryInfo
WHERE ciParent = @tmpID
ORDER BY ciLayer
END
擷取子分類的函數
public IList<CategoryInfo> GetChildCategories(IList<CategoryInfo> cInfos,string cName)
{
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("category_getChild", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter(PARAM_CNAME, SqlDbType.NVarChar, 20));
cmd.Parameters[PARAM_CNAME].Value = cName;
IList<string> tmpNames = new List<string>(); //臨時儲存擷取的子
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
CategoryInfo cInfo = new CategoryInfo(
(int)reader["ciID"],
reader["ciName"].ToString(),
(int)reader["ciParent"],
(int)reader["ciNum"],
reader["ciDescription"].ToString(),
(int)reader["ciLayer"]
);
string tmpName = reader["ciName"].ToString();
cInfos.Add(cInfo);//添加擷取到的分類到cInfos
tmpNames.Add(tmpName);//添加擷取到的子分類名到tmpNames
}
}
}
catch
{
throw new ApplicationException("擷取分類出錯!");
}
finally
{
con.Close();
}
foreach(string c in tmpNames)
{
cInfos = GetChildCategories(cInfos,c); //遞迴運算。繼續擷取子分類
}
return cInfos;
}
說明:在該函數中,tmpNames如果換成是IList<CategoryInfo>,即它添加的元素與cInfos是一樣的時,如果要移除其中的一項,則cInfos中會同時移除一項。因為CategoryInfo是類,是參考型別的,而非實值型別。所以tmpNames採用了string類型,以避免這個問題。
對上面這個函數直接調用還稍嫌麻煩,上層程式還需要建立一個IList<CategoryInfo>對象,因此可以增加一個函數來調用上面的函數。這樣,上層程式只需要提供分類名,以及是否包含本級分類兩個參數就可以了。
//擷取子分類,其中布爾參數表示是否包含本級分類
public IList<CategoryInfo> GetCategories( string cName, bool IsIncludeSelf)
{
IList<CategoryInfo> cInfos = new List<CategoryInfo>();
cInfos = GetChildCategories(cInfos, cName);
if (IsIncludeSelf == true)
{
cInfos.Insert(0, GetByName(cName));//根據名字擷取分類,這個很簡單,本文略。
}
return cInfos;
}
注意:採用這種方式時,WEB伺服器擷取子分類時要在資料庫伺服器之間有多次往返,降低了效能。採用預存程序實現遞迴邏輯,直接返回子分類列表的方式應該有更好的效能,尤其是Web伺服器與資料庫伺服器不位於同一台伺服器上時,更會受網路影響。