最近在CSDN上看到有朋友求Winform的三层架构的示例,网上也确实没有多少是Winform的。于是本人牺牲一点时间,做了这个小Demo,希抛砖引玉,望各路老手不吝指教。
至于什么是“三层结构”、三层结构有什么好处、为什么要用三层结构,我想这些讨论,网上大把,Google 或者 Baidu一下就有一大串。我也就不多说,直接用一个例子来讲述如何实现一个多层架构的WinForm程序。
在该例子,我选用的是VS.net 2005 + Access 实现,考虑至数据库的可移槙性,数据访问层使用工厂模式,利用.net 的反射在运行时根据配置文件来动态决定采用何用数据库。
好了,进入正题:
首先,设定数据库如下 :
对于,多层架构,我一般都按照预先新建一个空的方案。然后分别新加EntityReflect(实体层)、DALFactory(数据访问层,工厂类),Business(中间业务层)、WinForm(表示界面层),并设定WinForm为启始专案,该专案为WinForm模板,其它全部可作为Class Library模板,设定各专案的相依:Business依赖DALFactory、WinForm依赖Business,完了之后,选取整个方案,编译。在Business层引用DALFactory、WinForm层中引用Business.至上上面的数据访问层并没有针对特定的数据库的数据访问类,所以,再加一个DataAccess Class Library层,该层就是专门针对Access数据访问的。同样设定该层依赖DALFactory,并参考引用DALFactory.再次编译一次,我们就进入代码编制阶段了。
其实,我们上面的操作,已基本上具备了我上面所说了这个Demo的全部内容了,只是还没有代码而已啦。好,下面我们就添加代码:
打开EntityReflect专案,将默认的Class1.cs 改为EREmployee.cs,这就是我们的实体规范层,关于这个,我们可以在网上下载一些工具自动生成。不过,在我这个方案,我却一直没有在任何地方引用他。原因其实是我自己也不是很能弄清为什么一定要这个规范层。不过,我还是写了如下的代码:
展开DALFactory层.新加一个IDataAccessObject.cs,目的是为了规范各种数据库访问数据库时使用相同的方法,该接口的代码如下:
有了数据访问层的工厂,展开DataAccess数据访问层,将默认的Class1.cs 改为DataAccess.cs,输入以下代码:
展开Business层,将Class1.cs改为:BusinessLogic.cs,输入如下代码:
最后,展开WinFrom层,在窗体上拖一个DatagridView、两个label,两个textBox和两个button过来,Name属性分别改为:FrmMain、dgvMain、tbxBS_NO,tbxBS_NAME,btnQuery,btnSave,输入如下代码:
全部源码还实现了SQL Server的访问,由于Oracle没有用过,所以,在Oracle数据访问层中没有写任何代码,有会的朋友,请不吝回复在下面。先谢了。
顺便说一下,下载源码后,双击Build_Access.bat将编译整个方案,你可以至Winform中Bin目录下运行方案的效果。该BAT将建立一个访问Access数据的多层Winform程序,相应的Build_Oracle.bat,Build_SQLServer.bat分别对应于oracle和sql server.
Winform多层架构源码下载
ps:
欢迎各位加我QQ:52433739讨论,加时请注明C# Developer即可!
2007年3月14日又及:
今天下班,重新对这个小Demo进行了一番审视检查,觉得这个实体放在这里怪怪没有起任何作用,查了一些书籍和资料,我决定将该Demo重新编写,所幸不负期望!
确实,在这个小demo中,传递dataset是极不明确和含糊不清的,从而可能影响表示界面层的显示。为杜绝此类现象,终于发现了使用实体层的好处:那就是可规范所取得的数据的正确性。
鉴于此,我个人就觉得DALFactory(数据访问层,工厂类)、Business(中间业务层)、WinForm(表示界面层)都需引用这个EntityReflect(实体层)。同样的,我在方案中将他们三个分别依赖于EntityReflect(实体层)。
自然,相关的代码也将要进行修改,修改后的源码,请有兴趣的朋友请自行重新下载。
至于什么是“三层结构”、三层结构有什么好处、为什么要用三层结构,我想这些讨论,网上大把,Google 或者 Baidu一下就有一大串。我也就不多说,直接用一个例子来讲述如何实现一个多层架构的WinForm程序。
在该例子,我选用的是VS.net 2005 + Access 实现,考虑至数据库的可移槙性,数据访问层使用工厂模式,利用.net 的反射在运行时根据配置文件来动态决定采用何用数据库。
好了,进入正题:
首先,设定数据库如下 :
字段名称 |
数据类型 |
默认值 |
备注说明 |
BS_NO |
Text |
N'' |
工号 |
BS_NAME | Text |
N‘’ |
姓名 |
|
|
|
对于,多层架构,我一般都按照预先新建一个空的方案。然后分别新加EntityReflect(实体层)、DALFactory(数据访问层,工厂类),Business(中间业务层)、WinForm(表示界面层),并设定WinForm为启始专案,该专案为WinForm模板,其它全部可作为Class Library模板,设定各专案的相依:Business依赖DALFactory、WinForm依赖Business,完了之后,选取整个方案,编译。在Business层引用DALFactory、WinForm层中引用Business.至上上面的数据访问层并没有针对特定的数据库的数据访问类,所以,再加一个DataAccess Class Library层,该层就是专门针对Access数据访问的。同样设定该层依赖DALFactory,并参考引用DALFactory.再次编译一次,我们就进入代码编制阶段了。
其实,我们上面的操作,已基本上具备了我上面所说了这个Demo的全部内容了,只是还没有代码而已啦。好,下面我们就添加代码:
打开EntityReflect专案,将默认的Class1.cs 改为EREmployee.cs,这就是我们的实体规范层,关于这个,我们可以在网上下载一些工具自动生成。不过,在我这个方案,我却一直没有在任何地方引用他。原因其实是我自己也不是很能弄清为什么一定要这个规范层。不过,我还是写了如下的代码:
using System;
using System.Collections.Generic;
using System.Text;
namespace EntityReflect
{
public class EREmployee
{
public EREmployee()
{
}
private String strbs_no;
private String strbs_name;
public String strBS_NO
{
get { return strbs_no; }
set { strbs_no = value; }
}
public String strBS_NAME
{
get { return strbs_name; }
set { strbs_name = value; }
}
}
}
using System.Collections.Generic;
using System.Text;
namespace EntityReflect
{
public class EREmployee
{
public EREmployee()
{
}
private String strbs_no;
private String strbs_name;
public String strBS_NO
{
get { return strbs_no; }
set { strbs_no = value; }
}
public String strBS_NAME
{
get { return strbs_name; }
set { strbs_name = value; }
}
}
}
展开DALFactory层.新加一个IDataAccessObject.cs,目的是为了规范各种数据库访问数据库时使用相同的方法,该接口的代码如下:
using System;
using System.Data;
namespace DALFactory
{
public interface IDataAccessObject
{
void Query(DataSet ds, string strTableName);
void Save(string strBS_NO, string strBS_NAME);
}
}
另外,由于我使用了C#的RTTI,所以,还加了一个类GetDatabaseType.cs,代码如下:using System.Data;
namespace DALFactory
{
public interface IDataAccessObject
{
void Query(DataSet ds, string strTableName);
void Save(string strBS_NO, string strBS_NAME);
}
}
using System;
using System.Configuration;
using System.Reflection;
namespace DALFactory
{
public class GetDatabaseType
{
public IDataAccessObject GetDatabase()
{
string strAssemblyName = ConfigurationManager.AppSettings["AssemblyName"];
string strConstractor = ConfigurationManager.AppSettings["Constractor"];
return (IDataAccessObject)Assembly.Load(strAssemblyName).CreateInstance(strConstractor, false);
}
}
}
using System.Configuration;
using System.Reflection;
namespace DALFactory
{
public class GetDatabaseType
{
public IDataAccessObject GetDatabase()
{
string strAssemblyName = ConfigurationManager.AppSettings["AssemblyName"];
string strConstractor = ConfigurationManager.AppSettings["Constractor"];
return (IDataAccessObject)Assembly.Load(strAssemblyName).CreateInstance(strConstractor, false);
}
}
}
有了数据访问层的工厂,展开DataAccess数据访问层,将默认的Class1.cs 改为DataAccess.cs,输入以下代码:
using System;
using System.Data;
using System.Data.OleDb;
using DALFactory;
namespace DataAccess
{
public class DAOAccess:IDataAccessObject
{
private const string strConn = @"Provider = Microsoft.Jet.OLEDB.4.0;Data Source=D:\SHS_Develop\Solution\DB\Solution.mdb";
#region IDataAccessObject 成員
public void Query(System.Data.DataSet ds, string strTableName)
{
string strSql = @"Select * From Employee";
using (OleDbConnection Conn = new OleDbConnection(strConn))
{
using (OleDbDataAdapter adapter = new OleDbDataAdapter(strSql, Conn))
{
adapter.Fill(ds, strTableName);
}
}
}
public void Save(string strBS_NO, string strBS_NAME)
{
string strSql = @"Insert Into Employee(BS_NO,BS_NAME) Values(@BS_NO,@BS_NAME)";
using (OleDbConnection Conn = new OleDbConnection(strConn))
{
using (OleDbCommand Comm = new OleDbCommand(strSql, Conn))
{
Comm.Parameters.Add(new OleDbParameter("@BS_NO", OleDbType.VarChar,10));
Comm.Parameters.Add(new OleDbParameter("@BS_NAME", OleDbType.VarChar, 10));
Comm.Parameters["@BS_NO"].Value = strBS_NO;
Comm.Parameters["@BS_NAME"].Value = strBS_NAME;
Conn.Open();
Comm.ExecuteNonQuery();
Conn.Close();
}
}
}
#endregion
}
}
using System.Data;
using System.Data.OleDb;
using DALFactory;
namespace DataAccess
{
public class DAOAccess:IDataAccessObject
{
private const string strConn = @"Provider = Microsoft.Jet.OLEDB.4.0;Data Source=D:\SHS_Develop\Solution\DB\Solution.mdb";
#region IDataAccessObject 成員
public void Query(System.Data.DataSet ds, string strTableName)
{
string strSql = @"Select * From Employee";
using (OleDbConnection Conn = new OleDbConnection(strConn))
{
using (OleDbDataAdapter adapter = new OleDbDataAdapter(strSql, Conn))
{
adapter.Fill(ds, strTableName);
}
}
}
public void Save(string strBS_NO, string strBS_NAME)
{
string strSql = @"Insert Into Employee(BS_NO,BS_NAME) Values(@BS_NO,@BS_NAME)";
using (OleDbConnection Conn = new OleDbConnection(strConn))
{
using (OleDbCommand Comm = new OleDbCommand(strSql, Conn))
{
Comm.Parameters.Add(new OleDbParameter("@BS_NO", OleDbType.VarChar,10));
Comm.Parameters.Add(new OleDbParameter("@BS_NAME", OleDbType.VarChar, 10));
Comm.Parameters["@BS_NO"].Value = strBS_NO;
Comm.Parameters["@BS_NAME"].Value = strBS_NAME;
Conn.Open();
Comm.ExecuteNonQuery();
Conn.Close();
}
}
}
#endregion
}
}
展开Business层,将Class1.cs改为:BusinessLogic.cs,输入如下代码:
using System;
using DALFactory;
namespace Business
{
public class BusinessLogic
{
public void Query(System.Data.DataSet ds, string strTableName)
{
(new GetDatabaseType()).GetDatabase().Query(ds, strTableName);
}
public void Save(string strBS_NO, string strBS_NAME)
{
(new GetDatabaseType()).GetDatabase().Save(strBS_NO, strBS_NAME);
}
}
}
using DALFactory;
namespace Business
{
public class BusinessLogic
{
public void Query(System.Data.DataSet ds, string strTableName)
{
(new GetDatabaseType()).GetDatabase().Query(ds, strTableName);
}
public void Save(string strBS_NO, string strBS_NAME)
{
(new GetDatabaseType()).GetDatabase().Save(strBS_NO, strBS_NAME);
}
}
}
最后,展开WinFrom层,在窗体上拖一个DatagridView、两个label,两个textBox和两个button过来,Name属性分别改为:FrmMain、dgvMain、tbxBS_NO,tbxBS_NAME,btnQuery,btnSave,输入如下代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Business;
using RealBusiness;
namespace WinForm
{
public partial class FrmMain : Form
{
public FrmMain()
{
InitializeComponent();
}
private void btnQuery_Click(object sender, EventArgs e)
{
DataSet ds = new DataSet();
(new BusinessLogic()).Query(ds,"Employee");
this.dgvMain.DataSource = ds;
this.dgvMain.DataMember = "Employee";
}
private void btnSave_Click(object sender, EventArgs e)
{
if (!RBEmployee.CanRegistUser(tbxBS_NO.Text))
{
MessageBox.Show("The Business Logic Can't Allow to Add this user ");
return;
}
(new BusinessLogic()).Save(tbxBS_NO.Text, tbxBS_NAME.Text);
}
}
}
至此,可以说,一个简单的多层架构的Winform程序已完成,但如果有一些商业规则应如何加入呢,而且在现实世界中,商业规则是容易随宜主观的意愿而改变的。解决方法也是新加一个类。设为:RealBusiness层,class library模板,将Class1.cs改成:RBEmployee.cs,输入代码如下:using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Business;
using RealBusiness;
namespace WinForm
{
public partial class FrmMain : Form
{
public FrmMain()
{
InitializeComponent();
}
private void btnQuery_Click(object sender, EventArgs e)
{
DataSet ds = new DataSet();
(new BusinessLogic()).Query(ds,"Employee");
this.dgvMain.DataSource = ds;
this.dgvMain.DataMember = "Employee";
}
private void btnSave_Click(object sender, EventArgs e)
{
if (!RBEmployee.CanRegistUser(tbxBS_NO.Text))
{
MessageBox.Show("The Business Logic Can't Allow to Add this user ");
return;
}
(new BusinessLogic()).Save(tbxBS_NO.Text, tbxBS_NAME.Text);
}
}
}
using System;
namespace RealBusiness
{
public class RBEmployee
{
public static bool CanRegistUser(string strBS_NO)
{
if (strBS_NO.ToUpper() == "ADMIN" || strBS_NO.ToUpper() == "ADMINISTRATOR")
{
return false;
}
return true;
}
}
}
好了,至此,一个多层架构已完整实现,虽然功能不是很强大,但对于多层架构也基本上全涉及到了。所谓麻雀虽小,五脏俱全。有兴趣的朋友可以随后下载全部源码。namespace RealBusiness
{
public class RBEmployee
{
public static bool CanRegistUser(string strBS_NO)
{
if (strBS_NO.ToUpper() == "ADMIN" || strBS_NO.ToUpper() == "ADMINISTRATOR")
{
return false;
}
return true;
}
}
}
全部源码还实现了SQL Server的访问,由于Oracle没有用过,所以,在Oracle数据访问层中没有写任何代码,有会的朋友,请不吝回复在下面。先谢了。
顺便说一下,下载源码后,双击Build_Access.bat将编译整个方案,你可以至Winform中Bin目录下运行方案的效果。该BAT将建立一个访问Access数据的多层Winform程序,相应的Build_Oracle.bat,Build_SQLServer.bat分别对应于oracle和sql server.
Winform多层架构源码下载
ps:
欢迎各位加我QQ:52433739讨论,加时请注明C# Developer即可!
2007年3月14日又及:
今天下班,重新对这个小Demo进行了一番审视检查,觉得这个实体放在这里怪怪没有起任何作用,查了一些书籍和资料,我决定将该Demo重新编写,所幸不负期望!
确实,在这个小demo中,传递dataset是极不明确和含糊不清的,从而可能影响表示界面层的显示。为杜绝此类现象,终于发现了使用实体层的好处:那就是可规范所取得的数据的正确性。
鉴于此,我个人就觉得DALFactory(数据访问层,工厂类)、Business(中间业务层)、WinForm(表示界面层)都需引用这个EntityReflect(实体层)。同样的,我在方案中将他们三个分别依赖于EntityReflect(实体层)。
自然,相关的代码也将要进行修改,修改后的源码,请有兴趣的朋友请自行重新下载。