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

来源:互联网 发布:复杂网络中的幂律分布 编辑:程序博客网 时间:2024/05/01 04:38

前提: 

  年底用户提出新的需求,为了提高教学质量,某二级学院要求一个有个性的评教问卷,于是我就临时组建了一个攻坚小组,做一个评教系统。基本的需求是有单选题,多选题,填空题。目前多选题还没有实现,只是实现的单选题的功能。在做功能中,恰逢我们计算机一级考试系统验收。我有一点深刻的体会,那就是不要让你的系统进行太多次的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能使当前会话的查询,不受其它会话的事务所阻塞。但是这样做,就读取了其它事务没有执行完产生的脏数据。                                                                                                                                             



0 0
原创粉丝点击