這篇文章討論如何在c#中實現3層架構,使用MS Access數據庫存儲數據。在此,我在3層架構中實現一個小型的可復用的組件保存客戶數據。並提供添加,更新,查找客戶數據的功能。
背景
首先,我介紹一些3層架構的理論知識。簡單說明:什麼是3層架構?3層架構的優點是什麼?
什麼是3層架構?
3層架構是一種“客戶端-服務器”架構,在此架構中用戶接口,商業邏輯,數據保存以及數據訪問被設計為獨立的模塊。主要有3個層面,第一層(表現層,GUI層),第二層(商業對象,商業邏輯層),第三層(數據訪問層)。這些層可以單獨開發,單獨測試。
為什麼要把程序代碼分為3層,把用戶接口層,商業邏輯層,數據訪問層分離有許多的優點。
在快速開發中重用商業邏輯組件,我們已經在系統中實現添加,更新,刪除,查找客戶數據的組件。這個組件已經開發並且測試通過,我們可以在其他要保存客戶數據的項目中使用這個組件。
系統比較容易遷移,商業邏輯層與數據訪問層是分離的,修改數據訪問層不會影響到商業邏輯層。系統如果從用SQL Server存儲數據遷移到用Oracle存儲數據,並不需要修改商業邏輯層組件和GUI組件
系統容易修改,假如在商業層有一個小小的修改,我們不需要在用戶的機器上重裝整個系統。我們只需要更新商業邏輯組件就可以了。
應用程序開發人員可以並行,獨立的開發單獨的層。
代碼
這個組件有3層,第一個層或者稱為GUI層用form實現,叫做FrmGUI。第二層或者稱為商業邏輯層,叫做BOCustomer,是BussnIEss Object Customer的縮寫。最後是第三層或者稱為數據層,叫做DACustomer,是Data Access Customer的縮寫。為了方便我把三個層編譯到一個項目中。
用戶接口層
下面是用戶接口成的一段代碼,我只選取了調用商業邏輯層的一部分代碼。
//This function get the details from the user via GUI
//tIEr and calls the Add method of business logic layer.
private void cmdAdd_Click(object sender, System.EventArgs e)
{
try
{
cus = new BOCustomer();
cus.cusID=txtID.Text.ToString();
cus.LName = txtLName.Text.ToString();
cus.FName = txtFName.Text.ToString();
cus.Tel= txtTel.Text.ToString();
cus.Address = txtAddress.Text.ToString();
cus.Add();
}
catch(Exception err)
{
MessageBox.Show(err.Message.ToString());
}
}
//This function gets the ID from the user and finds the
//customer details and return the details in the form of
//a dataset via busniss object layer. Then it loops through
//the content of the dataset and fills the controls.
private void cmdFind_Click(object sender, System.EventArgs e)
{
try
{
String cusID = txtID.Text.ToString();
BOCustomer thisCus = new BOCustomer();
DataSet ds = thisCus.Find(cusID);
DataRow row;
row = ds.Tables[0].Rows[0];
//via looping
foreach(DataRow rows in ds.Tables[0].Rows )
{
txtFName.Text = rows["CUS_F_NAME"].ToString();
txtLName.Text = rows["CUS_L_NAME"].ToString();
txtAddress.Text = rows["CUS_ADDRESS"].ToString();
txtTel.Text = rows["CUS_TEL"].ToString();
}
}
catch (Exception err)
{
MessageBox.Show(err.Message.ToString());
}
}
//this function used to update the customer details.
private void cmdUpdate_Click(object sender,
System.EventArgs e)
{
try
{
cus = new BOCustomer();
cus.cusID=txtID.Text.ToString();
cus.LName = txtLName.Text.ToString();
cus.FName = txtFName.Text.ToString();
cus.Tel= txtTel.Text.ToString();
cus.Address = txtAddress.Text.ToString();
cus.Update();
}
catch(Exception err)
{
MessageBox.Show(err.Message.ToString());
}
}
商業邏輯層
下面是商業邏輯層的所有代碼,主要包括定義customer對象的屬性。但這僅僅是個虛構的customer對象,如果需要可以加入其他的屬性。商業邏輯層還包括添加,更新,查找,等方法。
商業邏輯層是一個中間層,處於GUI層和數據訪問層中間。他有一個指向數據訪問層的引用cusData = new DACustomer().而且還引用了System.Data名字空間。商業邏輯層使用DataSet返回數據給GUI層。
using System;
using System.Data;
namespace _3tIErarchitecture
{
/// <SUMMARY>
/// Summary description for BOCustomer.
/// </SUMMARY>
public class BOCustomer
{
//Customer propertIEs
private String fName;
private String lName;
private String cusId;
private String address;
private String tel;
private DACustomer cusData;
public BOCustomer()
{
//An instance of the Data Access layer!
cusData = new DACustomer();
}
/// <SUMMARY>
/// Property FirstName (String)
/// </SUMMARY>
public String FName
{
get
{
return this.fName;
}
set
{
try
{
this.fName = value;
if (this.fName == "")
{
throw new Exception(
"Please provide first name ...");
}
}
catch(Exception e)
{
throw new Exception(e.Message.ToString());
}
}
}
/// <SUMMARY>
/// Property LastName (String)
/// </SUMMARY>
public String LName
{
get
{
return this.lName;
}
set
{
//could be more checkings here eg revmove ' chars
//change to proper case
//blah blah
this.lName = value;
if (this.LName == "")
{
throw new Exception("Please provide name ...");
}
}
}
/// <SUMMARY>
/// Property Customer ID (String)
/// </SUMMARY>
public String cusID
{
get
{
return this.cusId;
}
set
{
this.cusId = value;
if (this.cusID == "")
{
throw new Exception("Please provide ID ...");
}
}
}
/// <SUMMARY>
/// Property Address (String)
/// </SUMMARY>
public String Address
{
get
{
return this.address;
}
set
{
this.address = value;
if (this.Address == "")
{
throw new Exception("Please provide address ...");
}
}
}
/// <SUMMARY>
/// Property Telephone (String)
/// </SUMMARY>
public String Tel
{
get
{
return this.tel;
}
set
{
this.tel = value;
if (this.Tel == "")
{
throw new Exception("Please provide Tel ...");
}
}
}
/// <SUMMARY>
/// Function Add new customer. Calls
/// the function in Data layer.
/// </SUMMARY>
public void Add()
{
cusData.Add(this);
}
/// <SUMMARY>
/// Function Update customer details.
/// Calls the function in Data layer.
/// </SUMMARY>
public void Update()
{
cusData.Update(this);
}
/// <SUMMARY>
/// Function Find customer. Calls the
/// function in Data layer.
/// It returns the details of the customer using
/// customer ID via a Dataset to GUI tIEr.
/// </SUMMARY>
public DataSet Find(String str)
{
if (str == "")
throw new Exception("Please provide ID to search");
DataSet data = null;
data = cusData.Find(str);
return data;
}
}
}
數據訪問層
數據層包括處理MS Access數據庫的細節。所有這些細節都是透明的,不會影響到商業邏輯層。數據訪問層有個指向商業邏輯層的引用BOCustomer cus。為了應用方便並且支持其他數據庫。
using System;
using System.Data.OleDb;
using System.Data;
namespace _3tIErarchitecture
{
/// <SUMMARY>
/// Summary description for DACustomer.
/// </SUMMARY>
public class DACustomer
{
private OleDbConnection cnn;
//change connection string as per the
//folder you unzip the files
private const string CnnStr =
"Provider=Microsoft.Jet.OLEDB.4.0;Data " +
"Source= D:\\Rahman_Backup\\Programming\\" +
"Csharp\\3tIErarchitecture\\customer.mdb;";
//local variables
private String strTable="";
private String strFIElds="";
private String strValues="";
private String insertStr="";
//this needs to be changed based on customer
//table fIElds' Name of the database!
private const String thisTable = "tblCustomer";
private const String cus_ID = "CUS_ID";
private const String cus_LName = "CUS_L_NAME";
private const String cus_FName = "CUS_F_NAME";
private const String cus_Tel = "CUS_TEL";
private const String cus_Address = "CUS_ADDRESS";
public DACustomer()
{
}
public DACustomer(BOCustomer cus)
{
// A reference of the business object class
}
//standard dataset function that adds a new customer
public void Add(BOCustomer cus)
{
String str = BuildAddString(cus);
OpenCnn();
//Open command option - cnn parameter is imporant
OleDbCommand cmd = new OleDbCommand(str,cnn);
//execute connection
cmd.ExecuteNonQuery();
// close connection
CloseCnn();
}
//standard dataset function that updates
//details of a customer based on ID
public void Update(BOCustomer cus)
{
OpenCnn();
String selectStr = "UPDATE " + thisTable +
" set " + cus_LName + " = '" + cus.LName + "'" +
", " + cus_FName + " = '" + cus.FName + "'" +
", " + cus_Address + " = '" + cus.Address + "'" +
", " + cus_Tel + " = '" + cus.Tel + "'" +
" where cus_ID = '" + cus.cusID + "'";
OleDbCommand cmd = new OleDbCommand(selectStr,cnn);
cmd.ExecuteNonQuery();
CloseCnn();
}
//standard dataset function that finds and
//return the detail of a customer in a dataset
public DataSet Find(String argStr)
{
DataSet ds=null;
try
{
OpenCnn();
String selectStr = "select * from " + thisTable +
" where cus_ID = '" + argStr + "'";
OleDbDataAdapter da =
new OleDbDataAdapter(selectStr,cnn);
ds = new DataSet();
da.Fill(ds,thisTable);
CloseCnn();
}
catch(Exception e)
{
String Str = e.Message;
}
return ds;
}
private void OpenCnn()
{
// initialise connection
String cnnStr = CnnStr;
cnn = new OleDbConnection(cnnStr);
// open connection
cnn.Open();
}
private void CloseCnn()
{
// 5- step five
cnn.Close();
}
// just a supporting function that builds
// and return the insert string for dataset.
private String BuildAddString(BOCustomer cus)
{
// these are the constants as
// set in the top of this module.
strTable="Insert into " + thisTable;
strFIElds=" (" + cus_ID +
"," + cus_LName +
"," + cus_FName +
"," + cus_Address +
"," + cus_Tel + ")";
//these are the attributes of the
//customer business object.
strValues= " Values ( '" + cus.cusID +
"' , '" + cus.LName +
"' , '" + cus.FName +
"' , '" + cus.Address +
"' , '" + cus.Tel + "' )";
insertStr = strTable + strFIElds + strValues;
return insertStr;
}
}
}