ADO.Net的封装已经有很多的实现了,但我总感觉那些实现还是没有透明化使用者对ADO.Net的了解。比如说很多人推崇的Enterprise Library的DataAccess,我认为就是封装不够彻底。我理想中封装彻底的ADO.Net对象是,使用者不需要(或尽可能的少)了解任何,而DataAccess还是需要使用者直接的处理很多ADO.Net的对象。而我需要的ADO.Net的封装希望使用者,仅给予SQL命令,赋值参数,并获取结果即可。
定义SqlDataProvider类
- /// <summary>
- /// SQL数据提供者的实现
- /// </summary>
- public class SqlDataProvider : DataProviders.IDataProvider
- {
- }
DataProviders.IDataProvider是我定义的一个接口,我希望将来所有的数据提供者都能实现该接口,以便利用依赖倒置实现抽象工厂。
定义DataProviders.IDataProvider接口
- public interface IDataProvider
- {
- void AddParameters(string parname, Guid value);
- void AddParameters(string parname, long value);
- void AddParameters(string parname, string value);
- void AddParameters(string parname, string value, DataProviders.StringFamily dataType);
- void AddParameters(string parname, string value, DataProviders.StringFamily dataType, int size);
- void AddParameters(string parname, float value);
- void AddParameters(string parname, decimal value);
- void AddParameters(string parname, DateTime value);
- void AddParameters(string parname, DateTime value, DataProviders.DateFamily dataType);
- void AddParameters(string parname, int value);
- void AddParameters(string parname, object value);
- void AddParameters(string parname, System.Drawing.Bitmap value);
- void AddParameters(string parname, byte[] value);
- void AddParameters(string parname, byte[] value, DataProviders.ByteArrayFamily dataType);
- void AddParameters(string parname, bool value);
- void AddParameters(string parname, short value);
- void AddParameters(string parname, byte value);
- System.Data.CommandType CommandType { get; set; }
- string ConnectionString { get; }
- System.Data.DataSet ExecuteDataSet();
- System.Data.DataTable ExecuteDataTable();
- void ExecuteReader(ReadData readData);
- int ExecuteNonQuery();
- object ExecuteScalar();
- string SQL { get; set; }
- }
- public delegate void ReadData(System.Data.IDataReader dataReadre);
从该接口可以看到,实现的DataProvider封装了关于具体的连接对象,命令对象和参数类型的信息。
SqlDataProvider类实现(基础部分)
- private static System.Data.SqlClient.SqlConnection conn;
- private System.Data.SqlClient.SqlCommand cmd;
- /// <summary>
- /// 默认构造函数
- /// </summary>
- public SqlDataProvider()
- {
- }
- /// <summary>
- /// 接受连接字符串
- /// </summary>
- /// <param name="connstr"></param>
- public SqlDataProvider(string connstr)
- : this(connstr, "")
- {
- }
- /// <summary>
- /// 接受连接字符串和sql字符串
- /// </summary>
- /// <param name="connstr"></param>
- /// <param name="sql"></param>
- public SqlDataProvider(string connstr, string sql)
- {
- conn = new System.Data.SqlClient.SqlConnection(connstr);
- cmd = new System.Data.SqlClient.SqlCommand();
- cmd.Connection = conn;
- cmd.CommandText = sql;
- }
- /// <summary>
- /// 需要执行的SQL命令
- /// </summary>
- public string SQL
- {
- set
- {
- cmd.CommandText = value;
- }
- get
- {
- return cmd.CommandText;
- }
- }
- /// <summary>
- /// 当前的连接字符串
- /// </summary>
- public string ConnectionString
- {
- get
- {
- return conn.ConnectionString;
- }
- }
- /// <summary>
- /// 设置命令的类型
- /// </summary>
- public System.Data.CommandType CommandType
- {
- set
- {
- cmd.CommandType = value;
- }
- get
- {
- return cmd.CommandType;
- }
- }
从上述代码可以观察到,我们的DataProvider只向用户暴露了两个字符串数据:连接字符串和SQL指令字符串。而将ADO.Net特有的Connection和Command封装起来了。由于用户不需要了解到具体实例化的Connection和Command,则为以后对其他数据源提供者的扩展带来了机会。
SqlDataProvider类实现(数据执行部分)
- /// <summary>
- /// 将SqlDataReader提交给具体的委托器处理
- /// </summary>
- /// <param name="readData"></param>
- public void ExecuteReader(ReadData readData)
- {
- using (conn)
- {
- conn.Open();
- System.Data.SqlClient.SqlDataReader dr = cmd.ExecuteReader();
- readData(dr);
- conn.Close();
- }
- }
- /// <summary>
- /// 对连接执行 Transact-SQL 语句并返回受影响的行数
- /// </summary>
- /// <returns></returns>
- public int ExecuteNonQuery()
- {
- int result = -1;
- using (conn)
- {
- conn.Open();
- result = cmd.ExecuteNonQuery();
- conn.Close();
- }
- return result;
- }
- /// <summary>
- /// 执行查询,并返回查询所返回的结果集中第一行的第一列。忽略其他列或行
- /// </summary>
- /// <returns></returns>
- public object ExecuteScalar()
- {
- object result = null;
- using (conn)
- {
- conn.Open();
- result = cmd.ExecuteScalar();
- conn.Close();
- }
- return result;
- }
- /// <summary>
- /// 执行查询,并返回查询的DataSet
- /// </summary>
- /// <returns></returns>
- public System.Data.DataSet ExecuteDataSet()
- {
- System.Data.DataSet datadet = new System.Data.DataSet();
- using (conn)
- {
- System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter();
- adapter.SelectCommand = cmd;
- conn.Open();
- adapter.Fill(datadet);
- conn.Close();
- }
- return datadet;
- }
- /// <summary>
- /// 执行查询,并返回查询的Table
- /// </summary>
- /// <param name="tableIndex"></param>
- /// <returns></returns>
- public System.Data.DataTable ExecuteDataSet(int tableIndex)
- {
- System.Data.DataSet datadet = ExecuteDataSet();
- if (datadet.Tables.Count > 0 && tableIndex < datadet.Tables.Count)
- {
- return datadet.Tables[tableIndex];
- }
- else
- {
- return null;
- }
- }
- /// <summary>
- /// 执行查询,并返回查询的Table
- /// </summary>
- /// <returns></returns>
- public System.Data.DataTable ExecuteDataTable()
- {
- System.Data.DataTable table = new System.Data.DataTable();
- using (conn)
- {
- System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter();
- adapter.SelectCommand = cmd;
- conn.Open();
- adapter.Fill(table);
- conn.Close();
- }
- return table;
- }
DataProvider提供ExecuteReader、ExecuteNonQuery、ExecuteScalar、ExecuteDataSet和ExecuteDataTable方法,向使用者封装了两种不同的信息:
对执行数据访问的过程(Open后要Close等)已经在执行过程中的辅助对象(DataAdapter)信息。使用者仅需要简单的调用上述的方法,既可以得到他所关注的数据。
SqlDataProvider类实现(参数部分)
- /// <summary>
- /// 添加一个Variant类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, object value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Variant).Value = value;
- }
- /// <summary>
- /// 添加一个Bit类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, bool value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Bit).Value = value;
- }
- /// <summary>
- /// 添加一个TinyInt类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, byte value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.TinyInt).Value = value;
- }
- /// <summary>
- /// 添加一个SmallInt类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, short value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.SmallInt).Value = value;
- }
- /// <summary>
- /// 添加一个Int类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, int value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Int).Value = value;
- }
- /// <summary>
- /// 添加一个BigInt类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, long value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.BigInt).Value = value;
- }
- /// <summary>
- /// 添加一张图片
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, System.Drawing.Bitmap value)
- {
- System.IO.MemoryStream ms = new System.IO.MemoryStream();
- value.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
- AddParameters(parname, ms.ToArray(), ByteArrayFamily.Image);
- }
- /// <summary>
- /// 添加一个Timestamp类型
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, byte[] value)
- {
- AddParameters(parname, value, ByteArrayFamily.Timestamp);
- }
- /// <summary>
- /// 添加一个字节数组族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="dateType"></param>
- public void AddParameters(string parname, byte[] value, ByteArrayFamily dataType)
- {
- cmd.Parameters.Add(parname, DataTypeAdapter.ConvertSqlDbType(dataType)).Value = value;
- }
- /// <summary>
- /// 添加一个字符类型数据,默认是NVarChar,长度是value.Length
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, string value)
- {
- AddParameters(parname, value, StringFamily.NVarChar, value.Length);
- }
- /// <summary>
- /// 添加一个字符族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="length"></param>
- public void AddParameters(string parname, string value, int size)
- {
- AddParameters(parname, value, StringFamily.NVarChar, size);
- }
- /// <summary>
- /// 添加一个字符族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="dateType"></param>
- /// <param name="length"></param>
- public void AddParameters(string parname, string value, StringFamily dataType)
- {
- AddParameters(parname, value,dataType, value.Length);
- }
- /// <summary>
- /// 添加一个字符族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="dateType"></param>
- /// <param name="size"></param>
- public void AddParameters(string parname, string value, StringFamily dataType, int size)
- {
- cmd.Parameters.Add(parname, DataTypeAdapter.ConvertSqlDbType(dataType), size).Value = value;
- }
- /// <summary>
- /// 添加一个SmallDateTime类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, DateTime value)
- {
- AddParameters(parname, value, DateFamily.SmallDateTime);
- }
- /// <summary>
- /// 添加一个日期族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="dateType"></param>
- public void AddParameters(string parname, DateTime value, DateFamily dataType)
- {
- cmd.Parameters.Add(parname, DataTypeAdapter.ConvertSqlDbType(dataType)).Value = value;
- }
- /// <summary>
- /// 添加一个Decimal类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, decimal value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Decimal).Value = value;
- }
- /// <summary>
- /// 添加Float类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, float value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Float).Value = value;
- }
- /// <summary>
- /// 添加一个UniqueIdentifier类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, System.Guid value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.UniqueIdentifier).Value = value;
- }
ADO.Net对参数的处理冗长的很,需要很多代码,我们的DataProvider通过重载简单的实现了对参数的处理,使用者在大多数情况下只需要提供两个参数:参数的名称和值。DataProvider和根据值的类型推算应该使用具体的哪个System.Data.SqlDbType。
不过,另人烦恼的是C#的数据类型和SQL的数据类型不是简单的一对一的关系,而是一对多的复杂关系,我们需要有一个方法来适配数据类型的不同。
数据类型的关系定义
- /// <summary>
- /// C#对于的SQL类型
- /// </summary>
- public enum StringFamily
- {
- Char,
- NChar,
- NText,
- NVarChar,
- Text,
- VarChar
- }
- /// <summary>
- /// C#对于的SQL类型
- /// </summary>
- public enum DateFamily
- {
- DateTime,
- SmallDateTime,
- Date,
- Time,
- DateTime2,
- DateTimeOffset
- }
- /// <summary>
- /// C#对于的SQL类型
- /// </summary>
- public enum ByteArrayFamily
- {
- Binary,
- Image,
- Timestamp,
- VarBinary
- }
DataTypeAdapter的定义
- /// <summary>
- /// SqlDbType数据类型和.NET Framework数据类型的适配器
- /// </summary>
- public static class DataTypeAdapter
- {
- /// <summary>
- /// 将.NET Framework数据类型适配为SqlDbType数据类型
- /// </summary>
- /// <param name="data"></param>
- /// <returns></returns>
- public static System.Data.SqlDbType ConvertSqlDbType(StringFamily data)
- {
- switch (data)
- {
- case StringFamily.Char:
- return System.Data.SqlDbType.Char;
- case StringFamily.NChar:
- return System.Data.SqlDbType.NChar;
- case StringFamily.NText:
- return System.Data.SqlDbType.NText;
- case StringFamily.NVarChar:
- return System.Data.SqlDbType.NVarChar;
- case StringFamily.Text:
- return System.Data.SqlDbType.Text;
- default:
- return System.Data.SqlDbType.VarChar;
- }
- }
- /// <summary>
- /// 将.NET Framework数据类型适配为SqlDbType数据类型
- /// </summary>
- /// <param name="data"></param>
- /// <returns></returns>
- public static System.Data.SqlDbType ConvertSqlDbType(DateFamily data)
- {
- switch (data)
- {
- case DateFamily.Date:
- return System.Data.SqlDbType.Date;
- case DateFamily.DateTime:
- return System.Data.SqlDbType.DateTime;
- case DateFamily.DateTime2:
- return System.Data.SqlDbType.DateTime2;
- case DateFamily.DateTimeOffset:
- return System.Data.SqlDbType.DateTimeOffset;
- case DateFamily.SmallDateTime:
- return System.Data.SqlDbType.SmallDateTime;
- default:
- return System.Data.SqlDbType.Time;
- }
- }
- /// <summary>
- /// 将.NET Framework数据类型适配为SqlDbType数据类型
- /// </summary>
- /// <param name="data"></param>
- /// <returns></returns>
- public static System.Data.SqlDbType ConvertSqlDbType(ByteArrayFamily data)
- {
- switch (data)
- {
- case ByteArrayFamily.Binary:
- return System.Data.SqlDbType.Binary;
- case ByteArrayFamily.Image:
- return System.Data.SqlDbType.Image;
- case ByteArrayFamily.Timestamp:
- return System.Data.SqlDbType.Timestamp;
- default:
- return System.Data.SqlDbType.VarBinary;
- }
- }
- }
通过上述的数据类型适配,我们将使用者和ADO.Net直接的具体关系弱耦合了。