引言: 参与了一个大型社区程序的开发,现在将相关开发经验陆续总结出来,和大家探讨一下。本节主要想与大家探讨一种数据读取方案:集合类代替直接从数据库中获取的DataSet,主要好处就是可以解决Sql Server吞吐量的瓶颈问题。一般小数量的程序不会有问题,但数据以十万百万条计的时候,数据库的吞吐量的限制就会表现的比较明显。这里的解决方案其实也就是把海量数据信息分成一条条取出,以频繁取库的代价解决瓶颈限制,其实也就是把数据库服务器的负担让WEB服务器分担了。闲话少说,进入正题,我们的例子还是社区程序中的一个配置表的读取,我们就是要根据一定条件来从数据库中获取数据。 一、数据库设计:
存储过程: /********************************** 功能:根据一定条件读取功能记录 作者:Rexsp 创建日期:2004-01-13 修改者: 修改日期: **********************************/ ALTER PROCEDURE GetScoreSetting ( @ScoreSettingID INT=-1, ---设置ID @FunctionID INT=-1, ---功能ID @OperationID INT=-1, ---操作ID @RoleTypeID INT=-1, ---角色类型 @BBSTypeID INT=-1, ---版块类型 @Score INT=-1, ---积分设置 @BB INT=-1, ---币币设置 @BuyType INT=-1, ---购买类型 0:不是购买类型 1:一次性购买 2:反复购买 @FunctionState INT=-1 ---功能状态 ) AS SET NOCOUNT ON DECLARE @strSQL NVARCHAR(1000) SET @strSQL = 'SELECT * FROM [ScoreSetting] WHERE @ckScoreSettingID = @ckScoreSettingID' --- Add KeyWords Begin --- IF @ScoreSettingID<> -1 BEGIN SET @strSQL = @strSQL + ' AND ScoreSettingID= @ckScoreSettingID' END IF @FunctionID<> -1 BEGIN SET @strSQL = @strSQL + ' AND FunctionID= @ckFunctionID' END IF @OperationID<>-1 BEGIN SET @strSQL = @strSQL + ' AND OperationID = @ckOperationID' END IF @RoleTypeID<>-1 BEGIN SET @strSQL = @strSQL + ' AND RoleTypeID = @ckRoleTypeID' END IF @BBSTypeID<>-1 BEGIN SET @strSQL = @strSQL + ' AND BBSTypeID = @ckBBSTypeID' END IF @Score<>-1 BEGIN SET @strSQL = @strSQL + ' AND Score = @ckScore' END IF @BB<>-1 BEGIN SET @strSQL = @strSQL + ' AND BB= @ckBB' END IF @BuyType<>-1 BEGIN SET @strSQL = @strSQL + ' AND BuyType= @ckBuyType' END IF @FunctionState<>-1 BEGIN SET @strSQL = @strSQL + ' AND FunctionState= @ckFunctionState' END --- Add Where Key Word --- --- Run SQL Begin --- EXECUTE sp_executesql @strSQL, N' @ckScoreSettingID INT, @ckFunctionID INT, @ckOperationID INT, @ckRoleTypeID INT, @ckBBSTypeID INT, @ckScore INT, @ckBB INT, @ckBuyType INT, @ckFunctionState INT', @ckScoreSettingID=@ScoreSettingID, @ckFunctionID=@FunctionID, @ckOperationID = @OperationID, @ckRoleTypeID = @RoleTypeID, @ckBBSTypeID = @BBSTypeID, @ckScore = @Score, @ckBB = @BB, @ckBuyType = @BuyType, @ckFunctionState = @FunctionState --- Run SQL End ---
一点说明: 此存储过程会根据数据层的类有没有传递相应的参数值进来而动态创建查询语句,然后用系统自带的存储过程执行sql语句,用系统存储过程执行sql语句的好处是可以自动转义字符。而动态创建查询语句的好处,就非常大了,这会省下我们写很多种条件判断,尤其是对那些字段比较多的表来讲,一个排列组合下来的情况太多了,而利用存储过程动态创建sql语句所作的判断数和字段数基本上是一致的,这里会给入参赋初始值,如果不等于初始值就说明数据层类有传递参数进来,这样就加上相应条件字符的查询条件。
二、 数据层类: using System; using System.Collections; using System.Data; using System.Data.SqlClient; using Town.Data; using Town.Log;
namespace Town.Com { /// /// 功能:积分设置集合类 /// 作者:Kelvin Yu /// 创建日期:2004-01-14 /// 修改者: /// 修改日期: /// public class ScoreSettingCollection { #region 私有成员 private ArrayList members; #endregion
#region 构造函数 /// /// 构造函数 /// public ScoreSettingCollection() { } #endregion
#region 公共属性 /// /// Operation对象的个数 /// public int Count { get { if( members != null ) return members.Count; else return 0; } } #endregion
#region 索引 /// /// 索引 /// public ScoreSetting this[int index] { get { if ( members != null ) return (ScoreSetting)( members[index] ); else return null; } } #endregion
#region 私有方法 /// /// 添加Operation到OperationCollection集合 /// private void Add(ScoreSetting scoreSetting) { if ( members == null ) members = new ArrayList(); members.Add( scoreSetting ); } #endregion
#region 公共方法 /// /// 根据不同条件取得积分设置 /// /// 功能ID /// 操作ID /// 角色ID /// 版块类型DI /// 积分 /// 币币 /// 购买类型 /// 功能状态 /// public bool GetSpecialInfo(int functionID,int operationID,int roleTypeID,int bBSTypeID,int score,int bB,int buyType,int functionState) { SqlDataAdapter dataAdapter = null; Database data = new Database("Town"); #region 创建参数 ArrayList sqlParameterList=new ArrayList(); if(functionID!=-1) sqlParameterList.Add(data.MakeInParam("@FunctionID", SqlDbType.Int, 4, functionID)); if(operationID!=-1) sqlParameterList.Add(data.MakeInParam("@OperationID", SqlDbType.Int, 4, operationID)); if(roleTypeID!=-1) sqlParameterList.Add(data.MakeInParam("@RoleTypeID", SqlDbType.Int, 4, roleTypeID)); if(bBSTypeID!=-1) sqlParameterList.Add(data.MakeInParam("@BBSTypeID", SqlDbType.Int, 4, bBSTypeID)); if(score!=-1) sqlParameterList.Add(data.MakeInParam("@Score", SqlDbType.Int, 4, score)); if(bB!=-1) sqlParameterList.Add(data.MakeInParam("@BB", SqlDbType.Int, 4, bB)); if(buyType!=-1) sqlParameterList.Add(data.MakeInParam("@BuyType", SqlDbType.Int, 4, buyType)); if(functionState!=-1) sqlParameterList.Add(data.MakeInParam("@FunctionState", SqlDbType.Int, 4, functionState));
SqlParameter[] prams= new SqlParameter[sqlParameterList.Count]; for( int i=0;i { prams[i]=(SqlParameter)sqlParameterList[i]; } #endregion try { data.RunProc("GetScoreSetting", prams, out dataAdapter); DataSet dataSet = new DataSet(); dataAdapter.Fill(dataSet,"table"); dataAdapter.Dispose(); if(dataSet.Tables["table"].Rows.Count == 0) { dataSet.Clear(); dataSet.Dispose(); return false; } else {
foreach(DataRow dr in dataSet.Tables["table"].Rows) { ScoreSetting SS = new ScoreSetting(); SS.ID= Int32.Parse(dr["ScoreSettingID"].ToString().Trim()); SS.FunctionID= Int32.Parse(dr["FunctionID"].ToString().Trim()); SS.OperationID= Int32.Parse(dr["OperationID"].ToString().Trim()); SS.RoleTypeID= Int32.Parse(dr["RoleTypeID"].ToString().Trim()); SS.BBSTypeID= Int32.Parse(dr["BBSTypeID"].ToString().Trim()); SS.Score= Int32.Parse(dr["Score"].ToString().Trim()); SS.BB= Int32.Parse(dr["BB"].ToString().Trim()); SS.BuyType= Int32.Parse(dr["BuyType"].ToString().Trim()); SS.FunctionState= Int32.Parse(dr["FunctionState"].ToString().Trim()); Add(SS); }
dataSet.Clear(); dataSet.Dispose();
return true; } } catch (Exception ex) { Error.Log("Town", ex.ToString()); dataAdapter.Dispose(); return false; } finally { data.Close(); data.Dispose();//释放Database } } #endregion
} }
一点说明: 数据层类分类的代码分了六块:私有成员、构造函数、公共属性、索引、私有方法、公有方法。这里为类建立了索引,这是集合类的必须元素。然后有一个私有方法,作用是把对象加到集合中,公有方法就是一个查询方法,上面的例子中是通过参数传的,其实也可以用属性传。这里作了个约定,如果传进的值为-1便认为此变量不起作用,基本跟存储过程中的思想是一样的。这个例子中的ScoreSetting对象是另外一个独立的类,如下:
using System; using System.Data; using System.Data.SqlClient; using Town.Data; using Town.Log; using System.Collections; namespace Town.Com { /// /// 功能:积分类 /// 作者:Kelvin Yu /// 创建日期:2004-01-14 /// 修改者: /// 修改日期: /// public class ScoreSetting {
#region 私有成员 /// /// 分值设置ID /// private int id=-1; /// /// 功能ID /// private int functionID=-1; /// /// 操作ID /// private int operationID=-1; /// /// 角色类型ID /// private int roleTypeID=-1; /// /// 版块类型ID /// private int bBSTypeID=-1; /// /// 积分 /// private int score=-2000000000; /// /// 币币 /// private int bB=-2000000000; /// /// 购买类型 /// private int buyType=-1; /// /// 功能状态 /// private int functionState=-1; /// /// 是否更新分值 /// #endregion
#region 公有属性
/// /// 积分设置DI /// public int ID { get{return id;} set{id=value;} } /// /// 功能ID /// public int FunctionID { get{return functionID;} set{functionID=value;} } /// /// 操作ID /// public int OperationID { get{return operationID;} set{operationID=value;} } /// /// 角色类型 /// public int RoleTypeID { get{return roleTypeID;} set{roleTypeID=value;} } /// /// 版块类型 /// public int BBSTypeID { get{return bBSTypeID;} set{bBSTypeID=value;} } /// /// 积分 /// public int Score { get{return score;} set{score=value;} } /// /// 币币 /// public int BB { get{return bB;} set{bB=value;} } /// /// 购买类型 0- 不是购买类型 1 - 一次性购买 2-反复购买 /// public int BuyType { get{return buyType;} set{buyType=value;} } /// /// 购买状态:0-无此功能 1-有此功能 2-需要购买 /// public int FunctionState { get{return functionState;} set{functionState=value;} } #endregion
#region 构造函数 public ScoreSetting() { } /// /// 重载构造函数 /// /// 积分设置ID public ScoreSetting(int id) { this.id=id; } #endregion
#region 公共方法 /// /// 根据ID获得积分设置信息 /// /// 成功true,失败false public bool GetInfoByID() { //代码略 }
/// /// 添加积分设置 /// /// 成功true,失败false public bool Add() { //代码略 } /// /// 编程积分设置 /// /// 成功返回true,失败返回false public bool Edit() { //代码略 } /// /// 删除积分设置 /// /// 成功返回true,失败返回false public bool Remove() { //代码略 } }
一点说明:这个类包含四部分,私有成员、构造函数、公共属性、公共方法,私有成员与数据库表的字段是对应的,属性与私有成员相对应。构造函数会初始数据表的key键,当然也可以重载构造函数初始另外的私有成员。这个类包含四个方法,也就是数据的四种操作:读、写、删、改。
三、表示层(UI层) 这一层主要是读取数据了。基本上会根据需求填充到不同的服务器控件中。
ScoreSettingCollection ssc = new ScoreSettingCollection(); FunctionCollection funcc = new FunctionCollection(); funcc.GetInfoByFunctionName("版名管理"); int functionID=funcc[0].ID; ssc.GetSpecialInfo(functionID,0,roleTypeID,bBSTypeID,-1,-1,-1,-1); int sscCount=ssc.Count;
上面的这段代码就是数据读取过程,根据sscCount可以判断有没有得到数据。在得到数据的前提下填充控件之前,是把这一条条的数据重新循环组成一个DataTable然后再绑定数据。 作者:rexsp | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|