zoukankan      html  css  js  c++  java
  • 利用SqlBulkCopy实现网上答题系统

    利用SqlBulkCopy实现网上答题系统

    摘 要 本文利用.Net 2.0引入的SqlBulkCopy新类结合SQL Server2005实现网上答题系统。介绍一种把其它数据源的数据批量加载到SQL Server表的高效方法,并确保其插入数据的完整性,一致性。

    关键字 .Net 2.0C#SqlBulkCopy,批量加载

     

    一、前言

    问卷调查是获取用户信息最有效途径之一,不管是企业单位,还是政府部门都广泛采用。

    利用网上这种途径来收集信息,可以节省大量的人力和物力。而同样有奖问题、网上考试也被各个企业单位广泛应用。其实现方式都可以通过网上答题系统来实现。  

    网上答题的题型主要有单项选择题,多项选择题和判断题。以往网上答题系统的实现方式是程序收集用户回答的答题信息,通过编写事务,把答题信息逐条的写入数据库。这样主要有两个问题。一是需要开发人员自己写事务来处理把答题信息插入数据库。如果处理不好会使收集的答题信息不完整,给开发人员带来不少的麻烦和开发难度。另外就是程序效率比较低下,特别是在题量比较大的情况下。通过.Net 2.0引入的SqlBulkCopy新类可以有效的解决这些问题。

    本文结合笔者的开发和应用实践,详细介绍利用SqlBulkCopy实现网上答题系统的原理和关键步骤。

    二、SqlBulkCopy介绍

    SqlBulkCopy.NET Framework 2.0新增的类,位于命名空间System.Data.SqlClient下,主要提供把其它数据源的数据有效批量的加载到SQL Server表中的功能。类似与 Microsoft SQL Server 包中名为 bcp 的命令行应用程序。但是使用 SqlBulkCopy 类可以编写托管代码解决方案,性能上优于bcp命令行应用程序,更优于如Insert方式向SQL Server表加载大量数据。SqlBulkCopy可以应用到大批量数据的转移上,而不管数据源是什么。

    三、数据库设计

    1、数据表设计

    打开SQL Server2005,建立数据表tUsertAnswer,分别是用户信息表和答题表。具体数据库关系和字段如图1所示:


    1 数据表及关系图

    具体思路是:用户根据答题内容回答完题,并填写完用户的个人信息。程序首先收集这些数据,并向用户信息表插入用户信息,返回该用户的编号(即UserID)。再根据用户编号生成内存表DataTable,利用SqlBulkCopy把内存表中数据批量的插入答题表。由数据提交的情况向用户返回成功或失败的消息。

    2、存储过程设计

    新增一个存储过程AddNewUser,完成根据姓名、电话和身份证号向用户信息表插入用户信息,返回自增量字段生成的用户ID号功能,具体代码如下:

    CREATE PROCEDURE [dbo].[AddNewUser]

    @fUserName varchar(50),

    @fPhone varchar(100),

    @fIDCard varchar(50),

    @fUserID int output—传出参数

    AS

    begin tran

    SET NOCOUNT ON;

    insert into tUser (fUserName,fPhone,fIDCard)values(@fUserName,@fPhone,@fIDCard);

    set @fUserID=(SELECT @@identity);--返回用户ID

    if @@error!=0   rollback

        else    commit

    四、程序实现

       根据图2制作网页,其中单项选择题和判断题使用RadioButtonList服务器控件,多项选择题使用CheckBoxList服务器控件。设置好各服务器控件的属性,题所使用控件的命名采用“题型+序号”的命名方式,如选择题第一题命名为:a1


    2 程序界面图

    随后添加“提交答卷”按钮的单击事件,核心代码如下:

    protected void Button1_Click(object sender, EventArgs e)

            {

                //构建内存答题表dt,用来存放获取的答题信息

                DataTable dt = new DataTable();

                dt.Columns.Add("UserID", typeof(int));

                dt.Columns.Add("QuestionID", typeof(int));

                dt.Columns.Add("Answer", typeof(string));

                int UserID = 0;

                int QuestionID = 0;

                string Answer = string.Empty;

                //采用遍历获取数据

                foreach (Control ctl in form1.Controls)

                {

                    //获取单选题、判断题的答题数据

                    if (ctl is RadioButtonList)

                    {

                        QuestionID = int.Parse(ctl.ID.Substring(1, ctl.ID.Length - 1));

                        Answer = ((RadioButtonList)ctl).SelectedValue;

              //把数据添加到构建的内存答题表dt

                        AddRow(ref dt, UserID, QuestionID, Answer);

                    }

                    if (ctl is CheckBoxList)

                    {

                        //获取多选题的答题数据

                        Answer = string.Empty;

                        QuestionID = int.Parse(ctl.ID.Substring(1, ctl.ID.Length - 1));

                        CheckBoxList cbl = (CheckBoxList)ctl;

                        for (int i = 0; i < cbl.Items.Count; i++)

                        {

                            if (cbl.Items[i].Selected)

                            {

                                Answer += cbl.Items[i].Value;

                            }

                        }

                        AddRow(ref dt, UserID, QuestionID, Answer);

                    }

                }

                //获取用户信息

                string UserName = txtUserName.Text;

                string Phone = txtPhone.Text;

                string IDCard = txtIDCard.Text;

                try

                {               

                    string spName = "AddNewUser";

            //省略部分是根据存储过程得到UserID

                       ……

                       //更新内存答题表dt信息

                    foreach (DataRow dr in dt.Rows)

                    {

                        dr["UserID"] = UserID;

                    }

            //通过SqlBulkCopy把内存答题表数据更新到Sql Server数据库中

                    SqlBulkCopyData(dt);

                    Label1.Text="提交答卷成功! ";

                }

                catch

                {

                   Label1.Text="系统错误,请和系统管理员联系! ";

                }

            }

    程序先在内存中构建用来存放答题信息的答题表dt,随后采用遍历的方式获取答题的数据,并通过AddRow()函数把获取的数据保存在构建的dt表中。AddRow()函数代码如下:

    protected void AddRow(ref DataTable dt, int UserID, int QuestionID, string Answer)

           {

              DataRow dr = dt.NewRow();

              dr["UserID"] = UserID;

              dr["QuestionID"] = QuestionID;

              dr["Answer"] = Answer;

              dt.Rows.Add(dr);

            }

    根据获取的答题数据调用存储过程AddNewUser保存用户信息,返回用户编号。更新答题表信息,通过SqlBulkCopy把答题表的数据批量加载到SQL Server数据库中,加载数据的代码如下:

            protected void SqlBulkCopyData(DataTable dt)

            {

                //使用SqlBulkCopy把内存表DataTable里的数据插入答卷数据表

                SqlBulkCopy bcp = new SqlBulkCopy(connectionString);

          //指定目标数据库的表名

                bcp.DestinationTableName = "tAnswer";

                //建立数据源表字段和目标表中的列之间的映射

                SqlBulkCopyColumnMapping MapUserID = new SqlBulkCopyColumnMapping();

                MapUserID.DestinationColumn = "fUserID";

                MapUserID.SourceColumn = "UserID";

                bcp.ColumnMappings.Add(MapUserID);

                SqlBulkCopyColumnMapping MapQID = new SqlBulkCopyColumnMapping();

                MapQID.DestinationColumn = "fQuestionID";

                MapQID.SourceColumn = "QuestionID";

                bcp.ColumnMappings.Add(MapQID);

                SqlBulkCopyColumnMapping MapAnswer = new SqlBulkCopyColumnMapping();

                MapAnswer.DestinationColumn = "fAnswer";

                MapAnswer.SourceColumn = "Answer";

                bcp.ColumnMappings.Add(MapAnswer);

                //写入数据库表

                bcp.WriteToServer(dt);

                bcp.Close();

            }

    SqlBulkCopy 包含一个方法 WriteToServer,它用来从数据的源复制数据到数据的目的地。WriteToServer方法可以处理的数据类型有DataRow[]数组、DataTable DataReader。根据实际情况,我们选用DataTable。程序先根据连接目标数据库的连接字符串生成SqlBulkCopy 实例,并指定DestinationTableName属性,也就是目标数据库的表名。再通过SqlBulkCopyColumnMapping类,设置数据源字段到目标数据表字段的映射。也就是说如果目标数据和源数据的列名不同时,可以用这个类进行映射。最后通过WriteToServer把数据加载到数据库中。

    如果目标表中的数据量比较大,需要设置SqlBulkCopyBatchSize,使加载数据分批进行,在每一批次结束时,就将该批次中的行发送到数据库。根据程序运行情况,给用户返回答卷提交结果。

    五、结束语

      以上程序在VS2005(C#)SQL Server 2005, Windows 2003 SP1环境下编译调试通过。利用.NET 2.0引入的SqlBulkCopy新类,实现对SQL Server数据库的批量加载。这种技术在实际开发项目网上答题系统中得到了充分利用。本系统根据各自的需求,增加答题内容,不用修改任何程序就可以用到实际的业务中。

    参考文献

    1MSDN文档

  • 相关阅读:
    区间dp_学习笔记
    状态压缩dp_学习笔记
    第十一届蓝桥杯C/C++ J题网络分析(带权并查集水题)
    状态机dp学习笔记_AcWing
    洛谷P4052 [JSOI2007]文本生成器(AC自动机+DP)
    洛谷P5840 [COCI2015]Divljak (AC自动机+fail树上dfs序+树上差分线段树维护)
    洛谷P3401 [USACO12JAN]Video Game G(AC自动机+记忆化搜索)
    HDU3613 Best Reward (exKMP/manacher)
    洛谷P2375 [NOI2014]动物园(KMP+倍增优化)
    ICPC2017南宁站题解(A,E,F,H,I,J,L,M)
  • 原文地址:https://www.cnblogs.com/13590/p/807065.html
Copyright © 2011-2022 走看看