天天看點

一、打造DataProvider一、打造DataProvider

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;  

/// 預設構造函數  

public SqlDataProvider()  

{   

/// 接受連接配接字元串  

/// <param name="connstr"></param>  

public SqlDataProvider(string connstr)  

: this(connstr, "")  

/// 接受連接配接字元串和sql字元串  

/// <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;  

/// 需要執行的SQL指令  

public string SQL  

set 

cmd.CommandText = value;  

get  

return cmd.CommandText;  

/// 目前的連接配接字元串  

public string ConnectionString  

return conn.ConnectionString;  

/// 設定指令的類型  

public System.Data.CommandType CommandType  

cmd.CommandType = value;  

return cmd.CommandType;  

從上述代碼可以觀察到,我們的DataProvider隻向使用者暴露了兩個字元串資料:連接配接字元串和SQL指令字元串。而将ADO.Net特有的Connection和Command封裝起來了。由于使用者不需要了解到具體執行個體化的Connection和Command,則為以後對其他資料源提供者的擴充帶來了機會。 

SqlDataProvider類實作(資料執行部分)

/// 将SqlDataReader送出給具體的委托器處理  

/// <param name="readData"></param>  

public void ExecuteReader(ReadData readData)  

using (conn)  

conn.Open();  

System.Data.SqlClient.SqlDataReader dr = cmd.ExecuteReader();  

readData(dr);  

conn.Close();  

/// 對連接配接執行 Transact-SQL 語句并傳回受影響的行數  

/// <returns></returns>  

public int ExecuteNonQuery()  

int result = -1;  

result = cmd.ExecuteNonQuery();  

return result;  

/// 執行查詢,并傳回查詢所傳回的結果集中第一行的第一列。忽略其他列或行  

public object ExecuteScalar()  

object result = null;  

result = cmd.ExecuteScalar();  

/// 執行查詢,并傳回查詢的DataSet  

public System.Data.DataSet ExecuteDataSet()  

System.Data.DataSet datadet = new System.Data.DataSet();  

System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter();  

adapter.SelectCommand = cmd;  

adapter.Fill(datadet);  

return datadet;  

/// 執行查詢,并傳回查詢的Table 

/// <param name="tableIndex"></param>  

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;  

public System.Data.DataTable ExecuteDataTable()  

System.Data.DataTable table = new System.Data.DataTable();  

adapter.Fill(table);  

return table;  

DataProvider提供ExecuteReader、ExecuteNonQuery、ExecuteScalar、ExecuteDataSet和ExecuteDataTable方法,向使用者封裝了兩種不同的資訊:

對執行資料通路的過程(Open後要Close等)已經在執行過程中的輔助對象(DataAdapter)資訊。使用者僅需要簡單的調用上述的方法,既可以得到他所關注的資料。

SqlDataProvider類實作(參數部分)

/// 添加一個Variant類型資料  

/// <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;  

/// 添加一個Bit類型資料  

public void AddParameters(string parname, bool value)  

cmd.Parameters.Add(parname, System.Data.SqlDbType.Bit).Value = value;  

/// 添加一個TinyInt類型資料  

public void AddParameters(string parname, byte value)  

cmd.Parameters.Add(parname, System.Data.SqlDbType.TinyInt).Value = value;  

/// 添加一個SmallInt類型資料  

public void AddParameters(string parname, short value)  

cmd.Parameters.Add(parname, System.Data.SqlDbType.SmallInt).Value = value;  

/// 添加一個Int類型資料  

public void AddParameters(string parname, int value)  

cmd.Parameters.Add(parname, System.Data.SqlDbType.Int).Value = value;  

/// 添加一個BigInt類型資料  

public void AddParameters(string parname, long value)  

cmd.Parameters.Add(parname, System.Data.SqlDbType.BigInt).Value = value;  

/// 添加一張圖檔  

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);  

/// 添加一個Timestamp類型  

public void AddParameters(string parname, byte[] value)  

AddParameters(parname, value, ByteArrayFamily.Timestamp);  

/// 添加一個位元組數組族類型資料  

/// <param name="dateType"></param>  

public void AddParameters(string parname, byte[] value, ByteArrayFamily dataType)  

cmd.Parameters.Add(parname, DataTypeAdapter.ConvertSqlDbType(dataType)).Value = value;  

/// 添加一個字元類型資料,預設是NVarChar,長度是value.Length  

public void AddParameters(string parname, string value)  

AddParameters(parname, value, StringFamily.NVarChar, value.Length);  

/// 添加一個字元族類型資料  

/// <param name="length"></param>  

public void AddParameters(string parname, string value, int size)  

AddParameters(parname, value, StringFamily.NVarChar, size);  

public void AddParameters(string parname, string value, StringFamily dataType)  

AddParameters(parname, value,dataType, value.Length);  

/// <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;  

/// 添加一個SmallDateTime類型資料  

public void AddParameters(string parname, DateTime value)  

AddParameters(parname, value, DateFamily.SmallDateTime);  

/// 添加一個日期族類型資料  

public void AddParameters(string parname, DateTime value, DateFamily dataType)  

/// 添加一個Decimal類型資料  

public void AddParameters(string parname, decimal value)  

cmd.Parameters.Add(parname, System.Data.SqlDbType.Decimal).Value = value;  

/// 添加Float類型資料  

public void AddParameters(string parname, float value)  

cmd.Parameters.Add(parname, System.Data.SqlDbType.Float).Value = value;  

/// 添加一個UniqueIdentifier類型資料  

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的資料類型不是簡單的一對一的關系,而是一對多的複雜關系,我們需要有一個方法來适配資料類型的不同。

資料類型的關系定義

/// C#對于的SQL類型  

public enum StringFamily  

Char,  

NChar,  

NText,  

NVarChar,  

Text,  

VarChar   

public enum DateFamily  

DateTime,  

SmallDateTime,  

Date,  

Time,  

DateTime2,  

DateTimeOffset   

public enum ByteArrayFamily  

Binary,  

Image,  

Timestamp,  

VarBinary  

DataTypeAdapter的定義 

/// SqlDbType資料類型和.NET Framework資料類型的擴充卡  

public static class DataTypeAdapter  

/// 将.NET Framework資料類型适配為SqlDbType資料類型  

/// <param name="data"></param>  

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;  

public static System.Data.SqlDbType ConvertSqlDbType(DateFamily 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;  

return System.Data.SqlDbType.Time;  

public static System.Data.SqlDbType ConvertSqlDbType(ByteArrayFamily 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;  

return System.Data.SqlDbType.VarBinary;  

通過上述的資料類型适配,我們将使用者和ADO.Net直接的具體關系弱耦合了。

本文轉自shyleoking 51CTO部落格,原文連結:http://blog.51cto.com/shyleoking/805857