将DataSet更新自动提交到数据库工具类实现

在项目中遇到一些要把DataSet的更改提交到数据库的需求,因此,实现了一个工具类来完成这个工作。

      设计思想:DataSet中的每一个DataTable中的每一行都有状态(RowState), 并且DataTable提供了一个GetChanges(DataRowState state)的方法来获取变化了的数据集合,返回一个新的DataTable对象。因此,我们根据数据提交的顺序,将分别处理 Insert, Update, Delete三种情况,并且根据与数据库表的映射,自动构造SQL语句,利用SqlDataAdapter完成数据提交工作。

      由于.net基础数据类型与SQL Server数据类型存在多对一的关系,且使用数据适配器(DataAdapter)时,构造SQL参数对象必须要求指定每个参数对象的长度以及更多信息(映射数据有版本的情况下),因此,不好根据DataColumn的类型来推导对应的SqlDbType,故根据需要实现了一个新的类,来保存每个列的映射及参数。

      下面是实现的原代码,有许多需要改进的地方,希望大家提出更好的意见和建议。在此谢过,先!
using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Text;

namespace kim.Data
{
    /// <summary>
    /// Summary description for SqlDbAccess.
    /// </summary>
    public class SqlDbAccess
    {
        static SqlDbAccess()
        {
        }

        /// <summary>
        /// 处理传入的数据库表名,加上[],避免因表名是数据库中的关键字而出错。
        /// 并且在不存在所有者名的时候,加上所有者名。
        /// </summary>
        /// <param name="dbTableName">待处理的数据库表名。</param>
        /// <returns>处理后的数据库表名。</returns>
        private static string PrepareDbTableName(string dbTableName)
        {
            string[] tableName = dbTableName.Split('.');
            if(tableName.Length == 1) //只有表名
            {
                if(tableName[0][0] == '[')
                    return tableName[0]; // [tableName] 格式
                else
                    return string.Concat("dbo.[", dbTableName, "]"); //tableName 格式
            }
            else
            {
                StringBuilder text = new StringBuilder(100);
                for(int i=0; i<tableName.Length; i++)
                {
                    if(tableName[0] == '[') //[xx] 格式
                        text.Append(tableName[0] + '.');
                    else // xx 格式
                        text.Append("[" + tableName + "].");
                }
                text.Remove(text.Length-1, 1);

                return text.ToString();
            }
        }

        /// <summary>
        /// 更新DataTable的更改到数据库中。
        /// 并发控制采用“最后的更新生效”
        /// </summary>
        /// <param name="table">有效的要提交的DataTable对象</param>
        /// <param name="sqlConn">有效的数据为连接对象</param>
        /// <param name="dbTableName">有效的数据库表名</param>
        /// <param name="dataColumnMappings">有效的要提交的列及参数映射对象数组</param>
        /// <param name="keyColumnMappings">有效的条件列及参数映射对象数组</param>
        /// <param name="needTransaction">是否需要事务</param>
        public static void UpdateData(DataTable table, SqlConnection sqlConn, string dbTableName,
            ParameterColumnMapping[] dataColumnMappings, ParameterColumnMapping[] keyColumnMappings, bool needTransaction)
        {
            if(table == null) throw new ArgumentNullException("table");
            if(sqlConn == null) throw new ArgumentNullException("sqlConn");
            if(dbTableName == null || dbTableName.Length == 0) throw new ArgumentNullException("dbTableName");
            if(dataColumnMappings == null || dataColumnMappings.Length == 0) throw new ArgumentNullException("dataColumnMappings");
            if(keyColumnMappings == null || keyColumnMappings.Length == 0) throw new ArgumentNullException("keyColumnMappings");
            if(table.Rows.Count == 0)    return;

            dbTableName = PrepareDbTableName(dbTableName);

            // 设置连接是否需要在完成后关闭的标记。
            // 如果连接对象传进来时是关闭的,则用完后也应该关闭。即保持连接传入前的状态。
            bool connNeedClose = (sqlConn.State == ConnectionState.Closed);

            SqlDataAdapter sqlAdapter = new SqlDataAdapter();

            // 添加表映射及列映射
            DataTableMapping tableMapping = sqlAdapter.TableMappings.Add(dbTableName, table.TableName);
            for(int i=0; i<dataColumnMappings.Length; i++)
            {
                tableMapping.ColumnMappings.Add(dataColumnMappings.SourceColumn, dataColumnMappings.DataTableColumn);
            }

            // 构造WHERE条件部分
            StringBuilder sqlWhere = new StringBuilder(256);
                sqlWhere = new StringBuilder(256);
                sqlWhere.Append(" WHERE ");
            for(int i=0; i<keyColumnMappings.Length; i++)
            {
                if(i == 0)
                {
                    sqlWhere.Append(string.Format("[{0}] = @{0}", keyColumnMappings.SourceColumn));
                }
                else
                {
                    sqlWhere.Append(string.Format(" AND [{0}] = @{0}", keyColumnMappings.SourceColumn));
                }
            }

            StringBuilder sqlText = new StringBuilder(1024);
            SqlTransaction sqlTran = null;
            DataTable tempTable = null;
            DataRow[] rows = new DataRow[1];
           
            // 开始提交数据
            try
            {
                if(connNeedClose) sqlConn.Open();

                if(needTransaction)    sqlTran = sqlConn.BeginTransaction();
                SqlCommand sqlCmd = new SqlCommand("", sqlConn, sqlTran);

                // 处理Insert
                tempTable = table.GetChanges(DataRowState.Added);
                if(tempTable != null)
                {
                    sqlAdapter.InsertCommand = sqlCmd;
                    StringBuilder valueText = new StringBuilder(256);
                    // 循环每一行,只处理不为Null的列
                    foreach(DataRow dr in tempTable.Rows)
                    {
                        sqlCmd.Parameters.Clear();
                        sqlText.Length = 0;
                        sqlText.Append("INSERT INTO " + dbTableName + " (");
                        for(int i=0; i<dataColumnMappings.Length; i++)
                        {
                            if(dr[dataColumnMappings.DataTableColumn] != DBNull.Value)
                            {
                                sqlText.Append(string.Format(" [{0}],", dataColumnMappings.SourceColumn));
                                valueText.Append(string.Format(" @{0},", dataColumnMappings.SourceColumn));
                                sqlCmd.Parameters.Add(dataColumnMappings.DbParameter);
                            }
                        }

                        // 移除末尾的','
                        sqlText.Remove(sqlText.Length-1, 1);
                        valueText.Remove(valueText.Length-1, 1);

                        // 完成Insert语句的构造并提交
                        sqlText.Append(string.Format(" ) VALUES ({0})", valueText.ToString()));
                        sqlCmd.CommandText = sqlText.ToString();
                        rows[0] = dr;
                        sqlAdapter.Update(rows);
                    }
                }

                // 处理Update
                // 仅对已经发生了改变的列进行Update
                tempTable = table.GetChanges(DataRowState.Modified);
                if(tempTable != null)
                {
                    int changedColumns = 0; //用于记录已经改变的列数
                    sqlAdapter.UpdateCommand = sqlCmd;
                    foreach(DataRow dr in tempTable.Rows)
                    {
                        sqlCmd.Parameters.Clear();
                        sqlText.Length = 0;
                        sqlText.Append(string.Format("UPDATE {0} SET", dbTableName));
                        changedColumns = 0;
                        for(int i=0; i<dataColumnMappings.Length; i++)
                        {
                            // 当前版本与原始版本不等
                            if(!dr[dataColumnMappings.DataTableColumn, DataRowVersion.Current].Equals(
                                dr[dataColumnMappings.DataTableColumn, DataRowVersion.Original]))
                            {
                                sqlText.Append(string.Format(" [{0}]=@{0},", dataColumnMappings.SourceColumn));
                                sqlCmd.Parameters.Add(dataColumnMappings.DbParameter);
                                changedColumns ++;
                            }
                        }

                        // 仅当有列改变了才进行处理,以防止行状态改变了,但值未变的情况。
                        if(changedColumns > 0)
                        {
                            sqlText.Remove(sqlText.Length-1, 1); //移除末尾的‘,'
                            sqlText.Append(" " + sqlWhere.ToString());

                            // 添加条件参数
                            for(int j=0; j<keyColumnMappings.Length; j++)
                            {
                                sqlCmd.Parameters.Add(keyColumnMappings[j].DbParameter);
                            }

                            // 提交数据
                            sqlCmd.CommandText = sqlText.ToString();
                            rows[0] = dr;
                            sqlAdapter.Update(rows);
                        }
                    }
                }

                // 处理Delete
                tempTable = table.GetChanges(DataRowState.Deleted);
                if(tempTable != null && tempTable.Rows.Count > 0)
                {
                    sqlText.Length = 0;
                    sqlText.Append("DELETE FROM " + dbTableName + sqlWhere.ToString());

                    sqlCmd.CommandText = sqlText.ToString();
                    sqlCmd.Parameters.Clear();
                    // 添加条件参数
                    for(int j=0; j<keyColumnMappings.Length; j++)
                    {
                        sqlCmd.Parameters.Add(keyColumnMappings[j].DbParameter);
                    }

                    sqlAdapter.DeleteCommand = sqlCmd;
                    sqlAdapter.Update(tempTable);
                }

                if(sqlTran != null)    sqlTran.Commit();
            }
            catch(Exception ex)
            {
                if(sqlTran != null)
                {
                    sqlTran.Rollback();
                }
                throw ex;
            }
            finally
            {
                if(connNeedClose)    sqlConn.Close();
            }
        }

        /// <summary>
        /// 定义参数与列的映射关系的类。
        /// </summary>
        public class ParameterColumnMapping
        {
            private string m_SourceColumn;
            private string m_DataTableColumn;
            private SqlParameter m_DbParameter;

            /// <summary>
            /// 构造函数。
            /// </summary>
            /// <param name="sourceColumn">数据库源列名</param>
            /// <param name="dataTableColumn">DataTable中的列名</param>
            /// <param name="dbParameter">对应的参数对象</param>
            public ParameterColumnMapping(string sourceColumn, string dataTableColumn, SqlParameter dbParameter)
            {
                m_SourceColumn = sourceColumn;
                m_DataTableColumn = dataTableColumn;
                m_DbParameter = dbParameter;
                SetParameterSourceColumn();
            }

            public ParameterColumnMapping() : this(null, null, null)
            {
            }

            /// <summary>
            /// 获取或设置数据库源列名。
            /// </summary>
            public string SourceColumn {
                get {
                    return m_SourceColumn;
                }
                set {
                    m_SourceColumn = value;
                }
            }

            /// <summary>
            /// 获取或设置DataTable对象中的列名。
            /// </summary>
            public string DataTableColumn {
                get {
                    return m_DataTableColumn;
                }
                set {
                    m_DataTableColumn = value;
                }
            }

            /// <summary>
            /// 获取或设置列对应的Sql参数对象。
            /// </summary>
            public SqlParameter DbParameter {
                get {
                    return m_DbParameter;
                }
                set {
                    m_DbParameter = value;
                    SetParameterSourceColumn();
                }
            }

            private void SetParameterSourceColumn()
            {
                if((m_DbParameter.SourceColumn == null || m_DbParameter.SourceColumn == string.Empty)
                    && (m_DataTableColumn != null && m_DataTableColumn != string.Empty))
                {
                    m_DbParameter.SourceColumn = m_DataTableColumn;
                }
            }
        }
    }
}
**************************************************

SqlDbAccess.ParameterColumnMapping[] dataColumnMappings = new kim.Data.SqlDbAccess.ParameterColumnMapping[2];
                dataColumnMappings[0] = new kim.Data.SqlDbAccess.ParameterColumnMapping("APP_ID", myData.AppSystems.AppIDColumn.ColumnName,
                    new SqlParameter("@APP_ID", SqlDbType.NVarChar, 20, myData.AppSystems.AppIDColumn.ColumnName));
                dataColumnMappings[1] = new kim.Data.SqlDbAccess.ParameterColumnMapping("DESCRIPTION", myData.AppSystems.DescriptionColumn.ColumnName,
                    new SqlParameter("@DESCRIPTION", SqlDbType.NVarChar, 500, myData.AppSystems.DescriptionColumn.ColumnName));

                SqlDbAccess.ParameterColumnMapping[] keyColumnMappings = new kim.Data.SqlDbAccess.ParameterColumnMapping[1];
                keyColumnMappings[0] = new kim.Data.SqlDbAccess.ParameterColumnMapping("APP_ID", myData.AppSystems.AppIDColumn.ColumnName,
                    new SqlParameter("@APP_ID", SqlDbType.NVarChar, 20, ParameterDirection.Input, true, 0, 0, myData.AppSystems.AppIDColumn.ColumnName, DataRowVersion.Original, null));

                SqlConnection sqlConn = new SqlConnection(Configuration.D2WHP01_ConnString);
                SqlDbAccess.UpdateData(myData.AppSystems, sqlConn, "AzProvider.dbo.APP", dataColumnMappings, keyColumnMappings, true);

ps: 缺点:使用sqlAdapter.Update,因为他是每条数据更新一次(效率比较低)

你可能感兴趣的:(将DataSet更新自动提交到数据库工具类实现)