C# 无限级分类的实现
程序员文章站
2022-04-05 22:47:59
数据库表:categoryinfo 字段名 类型 ciid int //记录序号,自增量 ciname nvarchar(20) //分类名 ciparent int //...
数据库表: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服务器与数据库服务器不位于同一台服务器上时,更会受网络影响。
字段名 类型
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服务器与数据库服务器不位于同一台服务器上时,更会受网络影响。