这篇文章讨论如何在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;
}
}
}
新闻热点
疑难解答