在項目中通常可能會使用不同的數據源,可能是SQL Server也可能是ACCESS或者是Oracle,那么如何保證在使用不同數據源的時候,使項目代碼更改的代價最小呢?
對,使用工廠模式.在Net1.1的時候,這需要項目實施者自己來完成.在Net2.0中,MS已經新增了幾個用於實施工廠模式的類庫.
首先我現在應用程序當前目錄下新建Databases目錄,再新建一個Access數據庫與Sqlserver數據庫
其中這2個數據庫的結構都是一樣的,都包含一個SampleData表,有ID,與IntegerValue字段
然后回到VS中新建一個WinForm 項目,然后編輯App.Config文件如下:
xml version="1.0" encoding="utf-8" ?> 這里記錄着實施項目中可能會用到的數據庫連接信息 providerName 記錄數據驅動 connectionString 數據源連接字符串 |DataDirectory|指應用程序當前目錄 --> 這里使用SqlServer2005直接在應用程序中附加數據庫 --> connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Databases\MyData.mdf;Integrated Security=True;User Instance=True" /> connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\Databases\MyData.mdb;Persist Security Info=True" /> connectionStrings> configuration> |
然后在主窗體中的Button事件中編寫如下代碼
private void getDataButton_Click(object sender, EventArgs e) { try { //創建一個新的StopWatch來監視連接性能[Net2.0新增] Stopwatch myWatch = new Stopwatch(); // 開始計算connection and retrieval of data所花費的時間 myWatch.Start(); // 選擇要使用的數據源 string strDataSource="MS Access"; // 根據選者的數據源獲得連接字符串的配置對象 ConnectionStringSettings objConnectionSettings = ConfigurationManager.ConnectionStrings[strDataSource]; // 通過配置文件創建數據庫驅動工廠的實例 DbProviderFactory objProviderFactory = DbProviderFactories.GetFactory(objConnectionSettings.ProviderName); // 通過數據庫驅動工廠創建DBConnection實例 using (DbConnection objConnection = objProviderFactory.CreateConnection()) { // 從 objConnectionSettings 中獲取連接字符串 objConnection.ConnectionString = objConnectionSettings.ConnectionString; // 打開 connection objConnection.Open(); // 通過數據驅動工廠創建 數據適配器和 Command DbDataAdapter myAdapter = objProviderFactory.CreateDataAdapter(); DbCommand myCommand = objProviderFactory.CreateCommand(); string myQuery = "SELECT * FROM SampleData"; DataSet myDataSet = new DataSet(); myCommand.Connection = objConnection; myCommand.CommandText = myQuery; myAdapter.SelectCommand = myCommand; myAdapter.Fill(myDataSet); displayDataGridView.DataSource = myDataSet.Tables[0]; // 停止StopWatch來查看連接和返回數據所花費的時間 myWatch.Stop(); elapsedTimeTextLabel.Text = "消耗時間: " myWatch.ElapsedMilliseconds.ToString() " ms"; providerLabel.Text = "數據驅動: " objConnectionSettings.ProviderName.ToString(); connectionStringLabel.Text = objConnectionSettings.ConnectionString.ToString(); } } catch { MessageBox.Show("出現錯誤.", "Alert"); } } } |
這樣,只需要更改strDataSource就可以使用不同的數據源,而且整個項目都不需要為不同的數據庫而煩惱
=====================================================================================================
使用設計模式構建通用數據庫訪問類
在應用程序的設計中,數據庫的訪問是非常重要的,我們通常需要將對數據庫的訪問集中起來,以保證良好的封裝性和可維護性。在.Net中,數據庫的訪問,對於微軟自家的SqlServer和其他數據庫(支持OleDb),采用不同的訪問方法,這些類分別分布於System.Data.SqlClient和System.Data.OleDb名稱空間中。微軟后來又推出了專門用於訪問Oracle數據庫的類庫。我們希望在編寫應用系統的時候,不因這么多類的不同而受到影響,能夠盡量做到數據庫無關,當后台數據庫發生變更的時候,不需要更改客戶端的代碼。
這就需要我們在實際開發過程中將這些數據庫訪問類再作一次封裝。經過這樣的封裝,不僅可以達到上述的目標,還可以減少操作數據庫的步驟,減少代碼編寫量。在這個方面,微軟為我們提供了Application Block,但是,可惜的是目前只支持Sql Server。這里,介紹一種在實際應用中得到了非常好的效果的實作策略——筆者編寫的Websharp框架中的數據訪問結構。Factory設計模式是使用的主要方法。
我們先來看看Factory的含義:定義一個用於創建對象的接口,讓子類決定實例化哪一個類。Factory Method使一個類的實例化延遲到其子類。我們這里可能會處理對多種數據庫的操作,因此,需要首先定義一個操縱數據庫的接口,然后,根據數據庫的不同,由類工廠決定實例化哪個類。
下面,我們首先來定義這個訪問接口。為了方便說明問題,我們在這里只列出了比較少的方法,其他的方法是很容易參照添加的。
public interface DataAccess { DatabaseType DatabaseType{get;} //數據庫類型 IDbConnection DbConnection{get;} //得到數據庫連接 void Open(); //打開數據庫連接 void Close(); //關閉數據庫連接 IDbTransaction BeginTransaction(); //開始一個事務 int ExecuteNonQuery(string commandText); //執行Sql語句 DataSet ExecuteDataset(string commandText);//執行Sql,返回DataSet } |
因為,DataAccess的具體實現類有一些共同的方法,所以,先從DataAccess實現一個抽象的AbstractDataAccess類,包含一些公用方法。然后,我們分別為Sql Server、Oracle和OleDb數據庫編寫三個數據訪問的具體實現類:
public sealed class MSSqlDataAccess : AbstractDataAccess { ……//具體實現代碼。 }
public class OleDbDataAccess : AbstractDataAccess { ……//具體實現代碼。 }
public class OracleDataAccess : AbstractDataAccess { ……//具體實現代碼。 } |
現在我們已經完成了所要的功能,下面,我們需要創建一個Factory類,來實現自動數據庫切換的管理。這個類很簡單,主要的功能就是根據數據庫類型,返回適當的數據庫操縱類。
public sealed class DataAccessFactory { private DataAccessFactory(){} private static PersistenceProperty defaultPersistenceProperty; public static PersistenceProperty DefaultPersistenceProperty { get{return defaultPersistenceProperty;} set{defaultPersistenceProperty=value;} } public static DataAccess CreateDataAccess(PersistenceProperty pp) { DataAccess dataAccess; switch(pp.DatabaseType) { case(DatabaseType.MSSQLServer): dataAccess = new MSSqlDataAccess(pp.ConnectionString); break; case(DatabaseType.Oracle): dataAccess = new OracleDataAccess(pp.ConnectionString); break; case(DatabaseType.OleDBSupported): dataAccess = new OleDbDataAccess(pp.ConnectionString); break; default: dataAccess=new MSSqlDataAccess(pp.ConnectionString); break; } return dataAccess; } public static DataAccess CreateDataAccess() { return CreateDataAccess(defaultPersistenceProperty); } } |
好了,現在,一切都完成了,客戶端在代碼調用的時候,可能就是采用如下形式:
PersistenceProperty pp = new PersistenceProperty(); pp.ConnectionString = "server=127.0.0.1;uid=sa;pwd=;database=Northwind;"; pp.DatabaseType = DatabaseType. MSSQLServer; pp.UserID = “sa”; pp.Password = “”; DataAccess db= DataAccessFactory.CreateDataAccess(pp) db.Open(); ……//db.需要的操作 db.Close();
或者,如果事先設定了DataAccessFactory的DefaultPersistenceProperty屬性,可以直接使用 DataAccess db= DataAccessFactory.CreateDataAccess() 方法創建DataAccess實例。 |
當數據庫發生變化的時候,只需要修改PersistenceProperty的值,客戶端不會感覺到變化,也不用去關心。這樣,實現了良好的封裝性。當然,前提是,你在編寫程序的時候,沒有用到特定數據庫的特性,例如,Sql Server的專用函數。
=================================================================================
數據庫訪問一般有不同中數據庫,比如Oracle,Sqlserver,Mysql等。 怎樣使得程序有一定的通用性。我們可以使用工廠模式來實現。具體代碼如下:
1 抽象類
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace HHSCInfor.App_Code.Database
...{
///
/// Summary description for absDB
///
public abstract class AbsDB
...{
public AbsDB()
...{
//
// TODO: Add constructor logic here
//
}
//得到數據庫連接
public abstract IDbConnection Connection ...{ get;}
//打開數據庫連接
public abstract void Open();
//關閉數據庫連接
public abstract void Close();
//開始一個事務
public abstract void BeginTrans();
//提交一個事務
public abstract void CommitTrans();
//回滾一個事務
public abstract void RollbackTrans();
//執行Sql語句,沒有返回值
public abstract void ExeSql(string strSql, string[] strParams, object[] objValues);
//執行Sql,返回DataSet
public abstract DataSet ExeSqlForDataSet(string QueryString);
}
}
2 Oracle連接的實例化
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.OracleClient;
namespace HHSCInfor.App_Code.Database
...{
///
/// Summary description for OracleDB
///
internal class OracleDB : AbsDB
...{
///
/// 數據庫連接狀態表示
///
private string OpenFlag = "OPEN";
public OracleDB()
...{
}
//數據庫連接
private OracleConnection conn;
//事務處理類
private OracleTransaction trans;
//指示當前是否正處於事務中
private bool inTransactionFlag = false;
public override IDbConnection Connection
...{
get ...{ return this.conn; }
}
public OracleDB(string strConnection)
...{
this.conn = new OracleConnection(strConnection);
}
public override void Open()
...{
if (conn.State.ToString().ToUpper() != OpenFlag)
this.conn.Open();
}
public override void Close()
...{
if (conn.State.ToString().ToUpper() == OpenFlag)
this.conn.Close();
}
public override void BeginTrans()
...{
trans = conn.BeginTransaction();
inTransactionFlag = true;
}
public override void CommitTrans()
...{
trans.Commit();
inTransactionFlag = false;
}
public override void RollbackTrans()
...{
trans.Rollback();
inTransactionFlag = false;
}
public override void ExeSql(string strSql, string[] strParams, object[] strValues)
...{
//創建命令
OracleCommand cmd = new OracleCommand();
//設置連接
cmd.Connection=this.conn ;
//比較參數個數和參數值數組的長度是否匹配
if ((strParams != null) && (strParams.Length != strValues.Length))
...{
throw new Exception("查詢參數和值不對應!");
}
cmd.CommandText = strSql;
if (strParams != null)
...{
for (int i = 0; i < strParams.Length; i )
...{
cmd.Parameters.Add(strParams[i], strValues[i]);
}
}
//執行SQL語句
cmd.ExecuteNonQuery();
}
///
/// 執行數據庫查詢並將結果用數據集(DataSet)的形式返回
///
/// SQL語句
///
public override DataSet ExeSqlForDataSet(string QueryString)
...{
//創建命令
OracleCommand cmd = new OracleCommand();
//設置連接
cmd.Connection = this.conn;
//傳入查詢語句
cmd.CommandText = QueryString;
//創建數據集
DataSet ds = new DataSet();
//創建適配器
OracleDataAdapter ad = new OracleDataAdapter();
//適配器命令
ad.SelectCommand = cmd;
//填充到數據集(DataSet)
ad.Fill(ds);
//返回結果數據集
return ds;
}
}
}
3 SqlServer的實例化(自己對照上面寫個,我也沒寫。^_^)
4 根據不同的string連接來創建不同的實例。
web.config中設置共用連接:
using System;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.OracleClient;
namespace HHSCInfor.App_Code.Database
...{
///
/// Summary description for DBConn
///
public class DBConn
...{
public DBConn()
...{
}
///
/// 根據不同的字符串連接來使用不同的處理程序。
/// 工廠方法應用(可以根據不同的strConnection創建不同的連接)。
///
/// 數據庫連接字符串
///
public static AbsDB GetDBConn()
...{
// 只有一個Oracle連接時使用,如果有多個,在此添加。在Web.Config里配置。
string strConnection = System.Configuration.ConfigurationManager.AppSettings["DBConnStr"];
// 創建OracleDB連接對象
return new OracleDB(strConnection);
///比如有sqlserver添加如下
///if(strConnection=?)
///{
/// return(SqlServerDB(strConnection));
///}
}
}
}
5 測試程序:
DataSet ds = conn.ExeSqlForDataSet("select * from sysFunction");
this.Label1.Text = ds.Tables[0].Rows[2]["功能名稱"].ToString();
conn.Close();
我的測試通過。