ado.net快速上手实践篇(一)

王朝学院·作者佚名  2010-05-27  
宽屏版  字体: 小 | 中 | 大 | 超大  

前言:这两天重温经典,对ado.net的东西稍微深入的了解了一下,顺便写点代码练练手,全当是复习笔记吧。

一、简单说说ado.net的5大常用对象

既然说ado.net,当然不能免俗地要提到5大常用对象。本文不会对ado.net的5大对象和它们的关系进行过多阐释,不过我们应该对下面这张图的结构有个了解:

关于上图图示中的5大对象,经常做以数据为驱动的mis系统的童鞋应该不会陌生。本文一笔带过。下面我们一步一步实现以ado.net为核心的数据访问程序。

【注意:下面的示例代码和demo是楼猪本周六和周日两天时间实现的,未经详细测试,可能有重大bug,下载学习使用的童鞋务必注意】

二、数据访问持久化层

1、IDbOperation接口

代码

using System.Collections.Generic;

using System.Data;

using System.Data.Common;

namespace AdoNetDataAccess.Core.Contract

{

public interface IDbOperation

{

DbCommand CreateDbCommd(DbConnection sqlConn, DbTransaction transaction, string sqlStr, CommandType cmdType, List<DbParameter> listParams);

DbParameter CreateDbPrameter(string paramName, object paramValue);

DbDataReader ExecuteReader(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

DataTable FillDataTable(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

DataSet FillDataSet(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

object ExecuteScalar(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

int ExecuteNonQuery(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

/// <summary>

/// 批量插入

/// </summary>

/// <param name="tableName">表名称</param>

/// <param name="dt">组装好的要批量导入的datatable</param>

/// <returns></returns>

bool ExecuteBatchInsert(string tableName, int batchSize, int copyTimeout, DataTable dt);

void OpenConnection();

void CloseConnection();

}

}

上面的接口包括增删改查,批量插入以及数据库连接对象的连接和关闭等常用操作,您可以根据命名和参数轻松理解函数的含义。根据楼猪的开发经验,对于平时的数据库操作,上述方法差不多够用了。当然您也可以按照自己需要,重写组织添加其他函数。

2、针对一种数据源的数据操作实现

底层的数据操作接口定义好后,就要针对一种数据源,具体实现上述的数据操作。这里楼猪选择了Sql Server。我们也可以实现其他数据源的数据访问操作,按照配置,利用抽象工厂动态反射选择是哪一种数据源的实现。这里按下不表,有心的童鞋自己可以动手一试。下面是具体的实现:

代码

using System;

using System.Collections.Generic;

using System.Data;

using System.Data.Common;

using System.Data.SqlClient;

using System.Transactions;

namespace AdoNetDataAccess.Core.Implement

{

using AdoNetDataAccess.Core.Contract;

public class SqlServer : IDbOperation, IDisposable

{

private int cmdTimeOut = 60;

private DbConnection sqlConn = null;

private DbCommand cmd = null;

private SqlServer()

{

}

public SqlServer(string sqlConStr)

{

sqlConn = new SqlConnection(sqlConStr);

cmdTimeOut = sqlConn.ConnectionTimeout;

}

public SqlServer(string sqlConStr, int timeOut)

{

sqlConn = new SqlConnection(sqlConStr);

if (timeOut < 0)

{

timeOut = sqlConn.ConnectionTimeout;

}

cmdTimeOut = timeOut;

}

#region contract method

public DbCommand CreateDbCommd(DbConnection sqlConn, DbTransaction transaction, string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

DbCommand cmd = new SqlCommand();

cmd.Connection = sqlConn;

cmd.CommandText = sqlStr;

cmd.CommandType = cmdType;

if (transaction != null)

{

cmd.Transaction = transaction;

}

if (listParams != null && listParams.Count > 0)

{

cmd.Parameters.AddRange(listParams.ToArray());

}

cmd.CommandTimeout = cmdTimeOut;

OpenConnection();

return cmd;

}

public DbParameter CreateDbPrameter(string paramName, object paramValue)

{

SqlParameter sp = new SqlParameter(paramName, paramValue);

return sp;

}

public DbDataReader ExecuteReader(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

DbDataReader rdr = null;

try

{

OpenConnection();

cmd = CreateDbCommd(sqlConn, null, sqlStr, cmdType, listParams);

rdr = cmd.ExecuteReader();

}

catch (Exception ex)

{

throw ex;

}

return rdr;

}

public DataTable FillDataTable(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

OpenConnection();

DbTransaction trans = sqlConn.BeginTransaction();

DbCommand cmd = CreateDbCommd(sqlConn, trans, sqlStr, cmdType, listParams);

SqlDataAdapter sqlDataAdpter = new SqlDataAdapter(cmd as SqlCommand);

DataTable dt = new DataTable();

try

{

sqlDataAdpter.Fill(dt);

trans.Commit();

}

catch (Exception e)

{

trans.Rollback();

throw new Exception("执行数据库操作失败, sql: " + sqlStr, e);

}

finally

{

sqlDataAdpter.Dispose();

cmd.Dispose();

trans.Dispose();

CloseConnection();

}

return dt;

}

public DataSet FillDataSet(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

OpenConnection();

DbTransaction trans = sqlConn.BeginTransaction();

DbCommand cmd = CreateDbCommd(sqlConn, trans, sqlStr, cmdType, listParams);

SqlDataAdapter sqlDataAdpter = new SqlDataAdapter(cmd as SqlCommand);

DataSet ds = new DataSet();

try

{

sqlDataAdpter.Fill(ds);

trans.Commit();

}

catch (Exception e)

{

trans.Rollback();

throw new Exception("执行数据库操作失败, sql: " + sqlStr, e);

}

finally

{

sqlDataAdpter.Dispose();

cmd.Dispose();

trans.Dispose();

CloseConnection();

}

return ds;

}

public object ExecuteScalar(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

object result = null;

OpenConnection();

DbTransaction trans = sqlConn.BeginTransaction();

try

{

cmd = CreateDbCommd(sqlConn, trans, sqlStr, cmdType, listParams);

result = cmd.ExecuteScalar();

trans.Commit();

}

catch (Exception e)

{

trans.Rollback();

throw new Exception("执行数据库操作失败, sql: " + sqlStr, e);

}

finally

{

trans.Dispose();

CloseConnection();

}

return result;

}

public int ExecuteNonQuery(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

int result = -1;

OpenConnection();

DbTransaction trans = sqlConn.BeginTransaction();

try

{

cmd = CreateDbCommd(sqlConn, trans, sqlStr, cmdType, listParams);

result = cmd.ExecuteNonQuery();

trans.Commit();

}

catch (Exception e)

{

trans.Rollback();

throw new Exception("执行数据库操作失败, sql: " + sqlStr, e);

}

finally

{

trans.Dispose();

CloseConnection();

}

return result;

}

/// <summary>

/// 批量插入

/// </summary>

/// <param name="tableName"></param>

/// <param name="batchSize"></param>

/// <param name="copyTimeout"></param>

/// <param name="dt"></param>

/// <returns></returns>

public bool ExecuteBatchInsert(string tableName, int batchSize, int copyTimeout, DataTable dt)

{

bool flag = false;

try

{

using (TransactionScope scope = new TransactionScope())

{

OpenConnection();

using (SqlBulkCopy sbc = new SqlBulkCopy(sqlConn as SqlConnection))

{

//服务器上目标表的名称

sbc.DestinationTableName = tableName;

sbc.BatchSize = batchSize;

sbc.BulkCopyTimeout = copyTimeout;

for (int i = 0; i < dt.Columns.Count; i++)

{

//列映射定义数据源中的列和目标表中的列之间的关系

sbc.ColumnMappings.Add(dt.Columns[i].ColumnName, dt.Columns[i].ColumnName);

}

sbc.WriteToServer(dt);

flag = true;

scope.Complete();//有效的事务

}

}

}

catch (Exception ex)

{

throw ex;

}

return flag;

}

public void OpenConnection()

{

if (sqlConn.State == ConnectionState.Broken || sqlConn.State == ConnectionState.Closed)

sqlConn.Open();

}

public void CloseConnection()

{

sqlConn.Close();

}

#endregion

#region dispose method

/// <summary>

/// dispose接口方法

/// </summary>

public void Dispose()

{

}

#endregion

}

}

到这里,我们实现了SqlServer类里的方法,对Ms SqlServer数据库我们就已经可以进行简单的基础的CRUD操作了。

三、简单直观的对象实体转换

在第二步中,我们已经实现了简单的数据CRUD操作。根据楼猪使用ORM的经验和习惯,我们也应该对一些查询结果进行转换,因为以类的组织方式比直接呈现ado.net对象更容易让人接受,效率高低反在其次。下面利用常见的反射原理,简单实现一个对象实体转换器ModelConverter类:

代码

using System;

using System.Collections;

using System.Collections.Generic;

using System.Data;

using System.Data.Common;

using System.Reflection;

using System.Threading;

namespace AdoNetDataAccess.Core.Obj2Model

{

using AdoNetDataAccess.Core.Contract;

public sealed class ModelConverter

{

private static readonly object objSync = new object();

#region query for list

/// <summary>

/// 查询数据表项并转换为对应实体

/// </summary>

/// <typeparam name="T"></typeparam>

/// <param name="objType"></param>

/// <param name="rdr"></param>

/// <returns></returns>

public static IList<T> QueryForList<T>(string sqlStr, CommandType cmdType, List<DbParameter> listParams, Type objType, IDbOperation dbOperation)

where T : class, new()

{

IDataReader rdr = dbOperation.ExecuteReader(sqlStr, cmdType, listParams);

IList<T> listModels = new List<T>();

try

{

Monitor.Enter(objSync);

Hashtable ht = CreateHashColumnName(rdr);

while (rdr.Read())

{

Object obj = Activator.CreateInstance(objType);

PropertyInfo[] properties = objType.GetProperties();

foreach (PropertyInfo propInfo in properties)

{

string columnName = propInfo.Name.ToUpper();

if (ht.ContainsKey(columnName) == false)

{

continue;

}

int index = rdr.GetOrdinal(propInfo.Name);

object columnValue = rdr.GetValue(index);

if (columnValue != System.DBNull.Value)

{

SetValue(propInfo, obj, columnValue);

}

}

T model = default(T);

model = obj as T;

listModels.Add(model);

}

}

finally

{

rdr.Close();

rdr.Dispose();

Monitor.Exit(objSync);

}

return listModels;

}

#endregion

#region query for dictionary

/// <summary>

/// 查询数据表项并转换为对应实体

/// </summary>

/// <typeparam name="K"></typeparam>

/// <typeparam name="T"></typeparam>

/// <param name="key">字典对应key列名</param>

/// <param name="objType"></param>

/// <param name="rdr"></param>

/// <returns></returns>

public static IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr, CommandType cmdType, List<DbParameter> listParams, Type objType, IDbOperation dbOperation)

where T : class, new()

{

IDataReader rdr = dbOperation.ExecuteReader(sqlStr, cmdType, listParams);

IDictionary<K, T> dictModels = new Dictionary<K, T>();

try

{

Monitor.Enter(objSync);

Hashtable ht = CreateHashColumnName(rdr);

while (rdr.Read())

{

Object obj = Activator.CreateInstance(objType);

PropertyInfo[] properties = objType.GetProperties();

object dictKey = null;

foreach (PropertyInfo propInfo in properties)

{

string columnName = propInfo.Name.ToUpper();

if (ht.ContainsKey(columnName) == false)

{

continue;

}

int index = rdr.GetOrdinal(propInfo.Name);

object columnValue = rdr.GetValue(index);

if (columnValue != System.DBNull.Value)

{

SetValue(propInfo, obj, columnValue);

if (string.Compare(columnName, key.ToUpper()) == 0)

{

dictKey = columnValue;

}

}

}

T model = default(T);

model = obj as T;

K objKey = (K)dictKey;

dictModels.Add(objKey, model);

}

}

finally

{

rdr.Close();

rdr.Dispose();

Monitor.Exit(objSync);

}

return dictModels;

}

#endregion

#region internal util

private static Hashtable CreateHashColumnName(IDataReader rdr)

{

int len = rdr.FieldCount;

Hashtable ht = new Hashtable(len);

for (int i = 0; i < len; i++)

{

string columnName = rdr.GetName(i).ToUpper(); //不区分大小写

string columnRealName = rdr.GetName(i);

if (ht.ContainsKey(columnName) == false)

{

ht.Add(columnName, columnRealName);

}

}

return ht;

}

private static void SetValue(PropertyInfo propInfo, Object obj, object objValue)

{

try

{

propInfo.SetValue(obj, objValue, null);

}

catch

{

object realValue = null;

try

{

realValue = Convert.ChangeType(objValue, propInfo.PropertyType);

propInfo.SetValue(obj, realValue, null);

}

catch (Exception ex)

{

string err = ex.Message;

//throw ex; //在数据库数据有不符合规范的情况下应该及时抛出异常

}

}

}

#endregion

}

}

到这里,简单的数据访问持久化层就实现了。下面模仿楼猪使用的IBatis.net,写个伪SqlMapper,改善一下调用形式,丰富一下调用方法,让方法辨识度更高。

四、实现伪SqlMapper

1、BaseMapper类

代码

using System;

using System.Collections.Generic;

using System.Data;

using System.Data.Common;

using System.Data.SqlClient;

namespace AdoNetDataAccess.Mapper

{

using AdoNetDataAccess.Core.Contract;

public abstract class BaseMapper

{

public IDbOperation CurrentDbOperation;

#region query for list

public abstract IList<T> QueryForList<T>(string sqlStr)

where T : class, new();

public abstract IList<T> QueryForList<T>(string sqlStr, Type objType)

where T : class, new();

public abstract IList<T> QueryForList<T>(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

where T : class, new();

public abstract IList<T> QueryForList<T>(string sqlStr, CommandType cmdType, List<DbParameter> listParams, Type objType)

where T : class, new();

#endregion

#region query for dictionary

public abstract IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr)

where T : class, new();

public abstract IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr, Type objType)

where T : class, new();

public abstract IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr, CommandType cmdType, Type objType)

where T : class, new();

public abstract IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr, CommandType cmdType, List<DbParameter> listParams, Type objType)

where T : class, new();

#endregion

#region dataset datatable

public abstract DataTable FillDataTable(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

public abstract DataSet FillDataSet(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

#endregion

#region ExecuteScalar

public abstract object ExecuteScalar(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

#endregion

#region insert

public abstract int Insert(string sqlStr);

public abstract int Insert(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

public abstract bool BatchInsert(string tableName, int batchSize, int copyTimeout, DataTable dt);

#endregion

#region delete

public abstract int Delete(string sqlStr);

public abstract int Delete(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

#endregion

#region update

public abstract int Update(string sqlStr);

public abstract int Update(string sqlStr, CommandType cmdType, List<DbParameter> listParams);

#endregion

}

}

上面代码中的方法您是不是很熟悉呢? 呵呵,使用IBatis.net 的童鞋应该会和楼猪产生更多的共鸣。

2、SqlMapper类

代码

using System;

using System.Collections.Generic;

using System.Data;

using System.Data.Common;

namespace AdoNetDataAccess.Mapper

{

using AdoNetDataAccess.Core.Contract;

using AdoNetDataAccess.Core.Obj2Model;

public class SqlMapper : BaseMapper

{

private SqlMapper()

{

}

public SqlMapper(IDbOperation dbOperation)

{

this.CurrentDbOperation = dbOperation;

}

#region query for list

public override IList<T> QueryForList<T>(string sqlStr)

{

return QueryForList<T>(sqlStr, CommandType.Text, null, typeof(T));

}

public override IList<T> QueryForList<T>(string sqlStr, Type objType)

{

return QueryForList<T>(sqlStr, CommandType.Text, null, objType);

}

public override IList<T> QueryForList<T>(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

return QueryForList<T>(sqlStr, cmdType, listParams, typeof(T));

}

public override IList<T> QueryForList<T>(string sqlStr, CommandType cmdType, List<DbParameter> listParams, Type objType)

{

return ModelConverter.QueryForList<T>(sqlStr, cmdType, listParams, objType, this.CurrentDbOperation);

}

#endregion

#region query for dictionary

public override IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr)

{

return QueryForDictionary<K, T>(key, sqlStr, CommandType.Text, null, typeof(T));

}

public override IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr, Type objType)

{

return QueryForDictionary<K, T>(key, sqlStr, CommandType.Text, null, objType);

}

public override IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr, CommandType cmdType, Type objType)

{

return QueryForDictionary<K, T>(key, sqlStr, cmdType, null, objType);

}

public override IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr, CommandType cmdType, List<DbParameter> listParams, Type objType)

{

return ModelConverter.QueryForDictionary<K, T>(key, sqlStr, cmdType, listParams, objType, this.CurrentDbOperation);

}

#endregion

#region dataset datatable

public override DataTable FillDataTable(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

return this.CurrentDbOperation.FillDataTable(sqlStr, cmdType, listParams);

}

public override DataSet FillDataSet(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

return this.CurrentDbOperation.FillDataSet(sqlStr, cmdType, listParams);

}

#endregion

#region ExecuteScalar

public override object ExecuteScalar(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

return this.CurrentDbOperation.ExecuteScalar(sqlStr, cmdType, listParams);

}

#endregion

#region insert

public override int Insert(string sqlStr)

{

object obj = ExecuteScalar(sqlStr, CommandType.Text, null);

int id = obj == null ? 0 : int.Parse(obj.ToString());

return id;

}

public override int Insert(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

object obj = ExecuteScalar(sqlStr, cmdType, listParams);

int id = obj == null ? 0 : int.Parse(obj.ToString());

return id;

}

/// <summary>

/// 批量插入

/// </summary>

/// <param name="tableName"></param>

/// <param name="batchSize"></param>

/// <param name="copyTimeout"></param>

/// <param name="dt"></param>

/// <returns></returns>

public override bool BatchInsert(string tableName, int batchSize, int copyTimeout, DataTable dt)

{

return this.CurrentDbOperation.ExecuteBatchInsert(tableName, batchSize, copyTimeout, dt);

}

#endregion

#region delete

public override int Delete(string sqlStr)

{

return CommitSql(sqlStr, CommandType.Text, null);

}

public override int Delete(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

return CommitSql(sqlStr, cmdType, listParams);

}

#endregion

#region update

public override int Update(string sqlStr)

{

return CommitSql(sqlStr, CommandType.Text, null);

}

public override int Update(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

return CommitSql(sqlStr, cmdType, listParams);

}

#endregion

#region commit and execute sql

private int CommitSql(string sqlStr, CommandType cmdType, List<DbParameter> listParams)

{

return this.CurrentDbOperation.ExecuteNonQuery(sqlStr, cmdType, listParams);

}

#endregion

#region dbparameter

public DbParameter CreateParameter(string paraName, object paramValue)

{

return this.CurrentDbOperation.CreateDbPrameter(paraName, paramValue);

}

public List<DbParameter> CreateParameterList(string[] paraNames, object[] paramValues)

{

List<DbParameter> listParams = new List<DbParameter>();

try

{

if (paraNames.Length != paramValues.Length)

{

throw new Exception("Param name and value is not equal.");

}

for (int i = 0; i < paraNames.Length; i++)

{

DbParameter param = CreateParameter(paraNames[i], paramValues[i]);

listParams.Add(param);

}

}

catch (Exception ex)

{

throw ex;

}

return listParams;

}

#endregion

}

}

上面的方法丰富实现了CRUD的常见操作,其实主要还是调用了IDbOperation接口和方法。

未完,待续。

demo下载:demo

http://www.cnblogs.com/jeffwongishandsome/archive/2010/05/23/1742002.html

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
© 2005- 王朝网络 版权所有