一、打造DataProvider
ADO.Net的封装已经有很多的实现了,但我总感觉那些实现还是没有透明化使用者对ADO.Net的了解。比如说很多人推崇的Enterprise Library的DataAccess,我认为就是封装不够彻底。我理想中封装彻底的ADO.Net对象是,使用者不需要(或尽可能的少)了解任何,而DataAccess还是需要使用者直接的处理很多ADO.Net的对象。而我需要的ADO.Net的封装希望使用者,仅给予SQL命令,赋值参数,并获取结果即可。
1.1定义DataProvide
定义SqlDataProvider类 |
/// <summary> /// SQL数据提供者的实现 /// </summary> public class SqlDataProvider : DataProviders.IDataProvider {
} |
1.2定义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封装了关于具体的连接对象,命令对象和参数类型的信息。
1.3实现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,则为以后对其他数据源提供者的扩展带来了机会。
1.4实现DataProvider数据执行部分
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)信息。使用者仅需要简单的调用上述的方法,既可以得到他所关注的数据。
1.5实现DataProvider参数部分
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。
1.6定义C#和SQL数据类型的关系
不过,另人烦恼的是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 } |
1.7定义数据类型的适配器
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直接的具体关系弱耦合了。