C# 无限级分类的实现
2020-01-18 01:23:38
供稿:网友
数据库表:CategoryInfo
字段名 类型
ciID int //记录序号,自增量
ciName nvarchar(20) //分类名
ciParent int //父分类序号
ciLayer int //所处的层次
ciDescription nvarchar(200) //对分类的描述
分类的类设计
public class CategoryInfo
{
private int ciID;//分类ID
private string ciName;//分类名
private int ciParent;//分类的父分类ID
private string ciDescription;//分类描述
private int ciLayer;//分类所属层次
//构造函数
public CategoryInfo() { }
public CategoryInfo(int cID, string cName, int cParent, string cDescription, int cLayer)
{
this.ciID = cID;
this.ciName = cName;
this.ciParent = cParent;
this.ciDescription = cDescription;
this.ciLayer = cLayer;
}
//属性
public int CategoryID
{
get{ return ciID;}
set { ciID = value;}
}
public string CategoryName
{
get{ return ciName;}
set { ciName = value; }
}
public int CategoryParent
{
get{ return ciParent;}
set { ciParent = value; }
}
public string CategoryDescription
{
get { return ciDescription; }
set { ciDescription = value; }
}
public int CategoryLayer
{
get { return ciLayer; }
set { ciLayer = value; }
}
}
获取子分类的存储过程
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"],
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服务器与数据库服务器不位于同一台服务器上时,更会受网络影响。