在项目中遇到一些要把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 description for SqlDbAccess.
///
public class SqlDbAccess
{
static SqlDbAccess()
{
}
///
/// 处理传入的数据库表名,加上[],避免因表名是数据库中的关键字而出错。
/// 并且在不存在所有者名的时候,加上所有者名。
///
/// 待处理的数据库表名。
///
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 { if(tableName text.Append(tableName else // xx 格式 text.Append("[" + tableName } text.Remove(text.Length-1, 1); return text.ToString(); } } /// /// 更新DataTable的更改到数据库中。 /// 并发控制采用“最后的更新生效” /// /// 有效的要提交的DataTable对象 /// 有效的数据为连接对象 /// 有效的数据库表名 /// 有效的要提交的列及参数映射对象数组 /// 有效的条件列及参数映射对象数组 /// 是否需要事务 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 { tableMapping.ColumnMappings.Add(dataColumnMappings } // 构造WHERE条件部分 StringBuilder sqlWhere = new StringBuilder(256); sqlWhere = new StringBuilder(256); sqlWhere.Append(" WHERE "); for(int i=0; i { if(i == 0) { sqlWhere.Append(string.Format("[{0}] = @{0}", keyColumnMappings } else { sqlWhere.Append(string.Format(" AND [{0}] = @{0}", keyColumnMappings } } 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 { if(dr[dataColumnMappings { sqlText.Append(string.Format(" [{0}],", dataColumnMappings valueText.Append(string.Format(" @{0},", dataColumnMappings sqlCmd.Parameters.Add(dataColumnMappings } } // 移除末尾的',' 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 { // 当前版本与原始版本不等 if(!dr[dataColumnMappings dr[dataColumnMappings { sqlText.Append(string.Format(" [{0}]=@{0},", dataColumnMappings sqlCmd.Parameters.Add(dataColumnMappings changedColumns ++; } } // 仅当有列改变了才进行处理,以防止行状态改变了,但值未变的情况。 if(changedColumns > 0) { sqlText.Remove(sqlText.Length-1, 1); //移除末尾的‘,' sqlText.Append(" " + sqlWhere.ToString()); // 添加条件参数 for(int j=0; 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 { 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(); } } /// /// 定义参数与列的映射关系的类。 /// public class ParameterColumnMapping { private string m_SourceColumn; private string m_DataTableColumn; private SqlParameter m_DbParameter; /// /// 构造函数。 /// /// 数据库源列名 /// DataTable中的列名 /// 对应的参数对象 public ParameterColumnMapping(string sourceColumn, string dataTableColumn, SqlParameter dbParameter) { m_SourceColumn = sourceColumn; m_DataTableColumn = dataTableColumn; m_DbParameter = dbParameter; SetParameterSourceColumn(); } public ParameterColumnMapping() : this(null, null, null) { } /// /// 获取或设置数据库源列名。 /// public string SourceColumn { get { return m_SourceColumn; } set { m_SourceColumn = value; } } /// /// 获取或设置DataTable对象中的列名。 /// public string DataTableColumn { get { return m_DataTableColumn; } set { m_DataTableColumn = value; } } /// /// 获取或设置列对应的Sql参数对象。 /// 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,因为他是每条数据更新一次(效率比较低)
[0] == '[') //[xx] 格式
[0] + '.');
+ "].");
.SourceColumn, dataColumnMappings
.DataTableColumn);
.SourceColumn));
.SourceColumn));
.DataTableColumn] != DBNull.Value)
.SourceColumn));
.SourceColumn));
.DbParameter);
.DataTableColumn, DataRowVersion.Current].Equals(
.DataTableColumn, DataRowVersion.Original]))
.SourceColumn));
.DbParameter);

