您的位置:首页 > 其它

评教系统优化之使用事务批量导入DataTable

2015-12-31 23:29 357 查看

前提: 

  年底用户提出新的需求,为了提高教学质量,某二级学院要求一个有个性的评教问卷,于是我就临时组建了一个攻坚小组,做一个评教系统。基本的需求是有单选题,多选题,填空题。目前多选题还没有实现,只是实现的单选题的功能。在做功能中,恰逢我们计算机一级考试系统验收。我有一点深刻的体会,那就是不要让你的系统进行太多次的IO操作,争取每一个的IO操作都得到最高的利用。

  评教系统相比考试系统而言数据量也是不相上下的。如果一条选择题都要去打开一次数据库连接池,那么再高配的数据库也承受不住100人的正常提交。我发现前辈就是前辈,看到评教代码,就想为前辈们点赞。评教数据的提交都是以整张表为单位进行的提交,比如说,一个考生完成了10道选择题,我们将创建一个DataTable来存放这10个选择题,然后一起提交整个DataTable,这样1次IO完成10条记录的insert,在性能上有了很大的提高。



实现代码:

1.前台提交评教结果按钮 代码:

protected void btnAddEvaluation_Click(object sender, EventArgs e)
{
//评分主页业务逻辑层
EvaluateTeacherBLL evaluateTeacherBLL = new EvaluateTeacherBLL();

//提交评教答案,跳入考试界面
#region 创建单项选择成绩表,并写入T_SingleAnswer--周洲--2015年12月31日
//创建选修课成绩表,并写入成绩
DataSet dsExperimentEvaluation = new DataSet("ds_ExperimentEvaluation"); //手动创建一个名为“ds_ExperimentEvaluation”的DataSet文件
DataTable dtExperimentEvaluation = new DataTable("dt_ExperimentEvaluation");
//为dt_ExperimentEvluation表内建立Column(表头),添加数据列:
dtExperimentEvaluation.Columns.Add(new DataColumn("studentID", typeof(string)));
dtExperimentEvaluation.Columns.Add(new DataColumn("singleChoiceID", typeof(string)));
dtExperimentEvaluation.Columns.Add(new DataColumn("singleAnswer", typeof(string)));
dtExperimentEvaluation.Columns.Add(new DataColumn("setDatetime", typeof(DateTime)));
dtExperimentEvaluation.Columns.Add(new DataColumn("isAvailable", typeof(string)));
//定义一个数组,存放隐藏控件(隐藏控件存放考核项目ID、选项ID、分数)
string[] oneExperimentAssessProject = hidScore.Value.Split('#');

//依次取出考核项目ID、选项ID、分数
for (int i = 1; i < oneExperimentAssessProject.Count(); i++)
{
string studentid = Session["StudentID"].ToString();
//考核项目ID
string singleChoiceID = oneExperimentAssessProject[i].Split('$')[0];
//选项ID
string singleOptionID = oneExperimentAssessProject[i].Split('$')[1];

#region  一条信息
//添加评分信息表的新行
DataRow drAddTheoryEvaluation = dtExperimentEvaluation.NewRow();//注意这边创建dt的新行的方法。指定类型是DataRow而不是TableRow,然后不用new直接的用创建的DataTable下面的NewRow方法。
//考核项目信息表对应的各列值
drAddTheoryEvaluation["studentID"] = studentid;
//学号列

drAddTheoryEvaluation["singleChoiceID"] = singleChoiceID; //考核项目ID
drAddTheoryEvaluation["singleAnswer"] = singleOptionID; //选项ID
drAddTheoryEvaluation["setDatetime"] = DateTime.Now; //当前日期时间

drAddTheoryEvaluation["isAvailable"] = "Y"; //是否可用
dtExperimentEvaluation.Rows.Add(drAddTheoryEvaluation);  //将一整条数据写入表中

#endregion
}
dsExperimentEvaluation.Tables.Add(dtExperimentEvaluation); //写入实验课成绩

//dsExperimentEvaluation.Tables.Add(dtSuggestion);  //希尔建议

#endregion

//将单选题答案传回数据库中
Boolean flagExpScore = evaluateTeacherBLL.AddSingleScore( dsExperimentEvaluation);

if (true == flagExpScore)
{

Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript' defer>alert('问卷提交成功!将跳入考试系统');</script>");
}
else
{
Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript' defer>alert('评分失败');</script>");

}
}

2.逻辑层,将DataTable送入,其中使用了事务来保证整个插入操作不会被打断,如果发生意外就回滚到执行插入之前的状态。

/// <summary>
/// 将选择题填入数据库中--周洲--2015年12月31日11:47:48
/// </summary>
/// <param name="enstudent"></param>
/// <param name="dsExpScore"></param>
/// <returns></returns>
public  Boolean AddSingleScore(DataSet  dsExpScore){

//  experimentProjectScoreDAL = new ExperimentProjectScoreDAL();
singleDAL =new SingleDAL();  //实例化单项选择题操作类
//定义事务执行所使用的链接
SqlConnection sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["strConnDB"].ConnectionString);

//打开连接
sqlCon.Open();
//定义事务
SqlTransaction sqlTran = sqlCon.BeginTransaction(IsolationLevel.ReadCommitted);
//用try...Catch...finally保证事务在出错时会回滚
try
{
//修改评估结果
//添加实验课成绩
//Boolean flagAddExpScore = experimentProjectScoreDAL.AddExperimentProjectScore(dsExpScore, sqlCon, sqlTran);

Boolean flagAddExpScore = singleDAL.AddSingleScore(dsExpScore, sqlCon, sqlTran);
// Boolean flagEditIsEvaluation = teacherCourseStudentLinkDAL.EditIsEvaluation(enTeacherCourseStudent, sqlCon, sqlTran);

//判断评分是否成功
if ( flagAddExpScore)
{
//如果都为真,提交
sqlTran.Commit();
return true;  //添加成功
}
else
{
sqlTran.Rollback();
return false; //添加失败
}
}
catch (Exception)
{
//出现异常时,事物回滚
sqlTran.Rollback();
return false;
}

finally
{
sqlCon.Close();
}
}


3.D层调用sqlhelper将记录送入数据库表里。

public Boolean AddSingleScore(DataSet dsScore, SqlConnection sqlCon, SqlTransaction sqlTran)            {
//定义布尔型标记变量,
//添加实验课成绩信息
Boolean flagAddExpScore;

//调用sqlHelper的"批量导入datatable表"的方法
flagAddExpScore = sqlHelper.InsertTable(dsScore.Tables["dt_ExperimentEvaluation"], "T_SingleAnswer", dsScore.Tables["dt_ExperimentEvaluation"].Columns, sqlCon, sqlTran);

//返回结果
return (flagAddExpScore);
}


   InserTable方法,传入表名,列名就可以了。

#region 批量导入DataTable 使用事务
/// <summary>
/// 批量导入DataTable 使用事务
/// </summary>
/// <param name="dt">DataTable数据表</param>
/// <param name="tableName">表名</param>
/// <param name="dtColum">列名</param>
public Boolean  InsertTable(DataTable dt, string tableName, DataColumnCollection dtColum, SqlConnection sqlConns, SqlTransaction sqlTran)
{
try
{
//声明SqlBulkCopy ,using释放非托管资源
using (SqlBulkCopy sqlBC = new SqlBulkCopy(sqlConns, SqlBulkCopyOptions.CheckConstraints, sqlTran))
{

//一次批量的插入的数据量
//sqlBC.BatchSize = 1000;
//超时之前操作完成所允许的秒数,如果超时则事务不会提交 ,数据将回滚,所有已复制的行都会从目标表中移除
//sqlBC.BulkCopyTimeout = 60;

//設定 NotifyAfter 属性,以便在每插入10000 条数据时,呼叫相应事件。
//sqlBC.NotifyAfter = 10000;
// sqlBC.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);

//设置要批量写入的表
sqlBC.DestinationTableName = tableName;

//自定义的datatable和数据库的字段进行对应
//sqlBC.ColumnMappings.Add("id", "tel");
//sqlBC.ColumnMappings.Add("name", "neirong");
for (int i = 0; i < dtColum.Count; i++)
{
sqlBC.ColumnMappings.Add(dtColum[i].ColumnName.ToString(), dtColum[i].ColumnName.ToString());
}
//批量写入
sqlBC.WriteToServer(dt);
}
//conn.Dispose();
//GetConn();
return true;
}
catch
{
return false;

}
}


总结:

1.尽量减少IO操作,提高IO操作的利用率。

2. 使用事务保证一组sql语句执行的完整性,避免造成很多的脏数据。

3. NOLOCK能使当前会话的查询,不受其它会话的事务所阻塞。但是这样做,就读取了其它事务没有执行完产生的脏数据。                                                                                                                                             
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: