三层架构是 C# 桌面开发中比较常用的框架,是由 表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三层架构组成,目的是为了 “高内聚,低耦合”。开发人员分工更明确,将精力更专注于应用系统核心业务逻辑的分析、设计和开发,加快项目的进度,提高了开发效率,有利于项目的更新和维护工作。
从三层架构可以看到,很类似于 Web 前端开发的 MVC 框架(视图View,模型Model,控制Contorller),但本质上也有不同的地方,比如都有视图(三层中叫 UI),Model ,三层中没有 Contorller,但 BLL 层和 Contorller 很类似。
以下是三层架构各层功能的详细分析:
(1)数据访问层:负责数据库的操作。
(2)业务逻辑层:实现功能模块的业务逻辑。
(3)界面UI层:绘制界面,以及负责界面相关代码。
(4)实体类:将数据库中的表转化为面向对象思想中的类。
例如写一个三层架构实现学生管理的案例:首先要创建一个窗体,然后就是数据和代码的准备
一、数据库准备:
--专业
create table ProfessionInfo
(
ProfessionID int primary key identity(1,1), --专业编号
ProfessionName varchar(50) not null unique --专业名称
)
--学生
create table StudentInfo
(
StuID varchar(20) primary key, --学生学号
StuName varchar(50) not null, --学生姓名
StuAge int not null check(StuAge > 0 and StuAge < 130), --学生年龄
StuSex char(2) not null check(StuSex in('男','女')), --学生性别
StuHobby nvarchar(100), --爱好
ProfessionID int not null references ProfessionInfo(ProfessionID), --所属专业编号
)
--添加专业信息
insert into ProfessionInfo(ProfessionName) values('电子竞技')
insert into ProfessionInfo(ProfessionName) values('软件开发')
insert into ProfessionInfo(ProfessionName) values('医疗护理')
--插入学生信息
insert into StudentInfo(StuID,StuName,StuAge,StuSex,StuHobby,ProfessionID)
values('001','刘备',18,'男','',1)
insert into StudentInfo(StuID,StuName,StuAge,StuSex,StuHobby,ProfessionID)
values('002','关羽',20,'男','',2)
insert into StudentInfo(StuID,StuName,StuAge,StuSex,StuHobby,ProfessionID)
values('003','张飞',19,'男','',2)
insert into StudentInfo(StuID,StuName,StuAge,StuSex,StuHobby,ProfessionID)
values('004','孙尚香',17,'女','',3)
二、项目内容
(1)创建一个空白解决方案。
(2)在解决方案中创建类库项目MyEntity代表"实体类"。
(3)在解决方案中创建类库项目MyDAL代表"数据访问层"。
(4)在解决方案中创建类库项目MyBLL代表"业务逻辑层"。
(5)在解决方案中创建Windows窗体应用程序MyUI代表"界面UI层"。
在MyEntity项目中添加两个实体类,实体类代码如下:
ProfessionInfoEntity:public class ProfessionInfoEntity
{
public ProfessionInfoEntity()
{
this.ProfessionID = 0;
this.ProfessionName = "";
}
public int ProfessionID { get; set; } //专业编号
public string ProfessionName { get; set; }//专业名称
}
StudentInfoEntiy:
public class StudentInfoEntiy
{
public StudentInfoEntiy()
{
this.StuID = "";
this.StuName = "";
this.StuAge = 0;
this.StuSex = "";
this.StuHobby = "";
this.ProfessionID = 0;
this.ProfessionName = "";
}
public string StuID { get; set; } //学生学号
public string StuName { get; set; } //学生姓名
public int StuAge { get; set; } //学生年龄
public string StuSex { get; set; } //学生性别
public string StuHobby { get; set; } //学生爱好
public int ProfessionID { get; set; } //学生所属专业编号
public string ProfessionName { get; set; } //学生所属专业名称
}
三、数据访问层编写
(1)由于数据访问层需要使用实体类,所以需要添加实体类的引用。
即在MyDAL项目上右键-->添加-->引用-->项目,在项目中勾选MyEntity项目。
(2)将之前封装好的DBHelper文件复制到MyDAL项目中,并通过添加现有项,将DBHelper加入项目。
(3)在MyDAL项目中添加两个类,类代码如下:
ProfessionInfoDAL:
public class ProfessionInfoDAL
{
#region 新增
public int Add(ProfessionInfoEntity entity)
{
string sql = "insert into ProfessionInfo(professionName) values(@professionName)";
DBHelper.PrepareSql(sql);
DBHelper.SetParameter("ProfessionName",entity.ProfessionName);
return DBHelper.ExecNonQuery();
}
#endregion
#region 删除
public int Delete(int id)
{
string sql = "delete from ProfessionInfo where ProfessionID=@ProfessionID";
DBHelper.PrepareSql(sql);
DBHelper.SetParameter("ProfessionID", id);
return DBHelper.ExecNonQuery();
}
#endregion
#region 修改
public int Update(ProfessionInfoEntity entity)
{
string sql = "update ProfessionInfo set professionName=@professionName where ProfessionID=@ProfessionID";
DBHelper.PrepareSql(sql);
DBHelper.SetParameter("professionName", entity.ProfessionName);
DBHelper.SetParameter("ProfessionID", entity.ProfessionID);
return DBHelper.ExecNonQuery();
}
#endregion
#region 列表
public List<ProfessionInfoEntity> List()
{
string sql = "select * from ProfessionInfo";
DataTable dt = new DataTable();
DBHelper.PrepareSql(sql);
dt = DBHelper.ExecQuery();
List<ProfessionInfoEntity> list = new List<ProfessionInfoEntity>();
foreach (DataRow item in dt.Rows)
{
ProfessionInfoEntity entity = new ProfessionInfoEntity();
entity.ProfessionID = int.Parse(item["ProfessionID"].ToString());
entity.ProfessionName = item["ProfessionName"].ToString();
list.Add(entity);
}
return list;
}
#endregion
#region 详情
public ProfessionInfoEntity Detail(int id)
{
string sql = "select * from ProfessionInfo where ProfessionID=@ProfessionID";
DataTable dt = new DataTable();
DBHelper.PrepareSql(sql);
DBHelper.SetParameter("ProfessionID", id);
dt = DBHelper.ExecQuery();
if (dt.Rows.Count == 0)
return null;
ProfessionInfoEntity entity = new ProfessionInfoEntity();
entity.ProfessionID = int.Parse(dt.Rows[0]["ProfessionID"].ToString());
entity.ProfessionName = dt.Rows[0]["ProfessionName"].ToString();
return entity;
}
#endregion
}
StudentInfoDAL:
public class StudentInfoDAL
{
#region 新增
public int Add(StudentInfoEntiy entity)
{
string sql = "insert into StudentInfo(StuID,StuName,StuAge,StuSex,StuHobby,ProfessionID) values(@StuID,@StuName,@StuAge,@StuSex,@StuHobby,@ProfessionID)";
DBHelper.PrepareSql(sql);
DBHelper.SetParameter("StuID", entity.StuID);
DBHelper.SetParameter("StuName", entity.StuName);
DBHelper.SetParameter("StuAge", entity.StuAge);
DBHelper.SetParameter("StuSex", entity.StuSex);
DBHelper.SetParameter("StuHobby", entity.StuHobby);
DBHelper.SetParameter("ProfessionID", entity.ProfessionID);
return DBHelper.ExecNonQuery();
}
#endregion
#region 删除
public int Delete(string id)
{
string sql = "delete from StudentInfo where StuID=@StuID";
DBHelper.PrepareSql(sql);
DBHelper.SetParameter("StuID", id);
return DBHelper.ExecNonQuery();
}
#endregion
#region 修改
public int Update(StudentInfoEntiy entity)
{
string sql = "update StudentInfo set StuName=@StuName,StuAge=@StuAge,StuSex=@StuSex,StuHobby=@StuHobby,ProfessionID=@ProfessionID where StuID=@StuID";
DBHelper.PrepareSql(sql);
DBHelper.SetParameter("StuName", entity.StuName);
DBHelper.SetParameter("StuAge", entity.StuAge);
DBHelper.SetParameter("StuSex", entity.StuSex);
DBHelper.SetParameter("StuHobby", entity.StuHobby);
DBHelper.SetParameter("ProfessionID", entity.ProfessionID);
DBHelper.SetParameter("StuID", entity.StuID);
return DBHelper.ExecNonQuery();
}
#endregion
#region 列表
public List<StudentInfoEntiy> List()
{
string sql = "select * from StudentInfo";
DataTable dt = new DataTable();
DBHelper.PrepareSql(sql);
dt = DBHelper.ExecQuery();
List<StudentInfoEntiy> list = new List<StudentInfoEntiy>();
foreach (DataRow item in dt.Rows)
{
StudentInfoEntiy entity = new StudentInfoEntiy();
entity.StuID = item["StuID"].ToString();
entity.StuName = item["StuName"].ToString();
entity.StuAge = int.Parse(item["StuAge"].ToString());
entity.StuSex = item["StuSex"].ToString();
entity.StuHobby = item["StuHobby"].ToString();
entity.ProfessionID = int.Parse(item["ProfessionID"].ToString());
list.Add(entity);
}
return list;
}
#endregion
#region 详情
public StudentInfoEntiy Detail(string id)
{
string sql = "select * from StudentInfo where StuID=@StuID";
DataTable dt = new DataTable();
DBHelper.PrepareSql(sql);
DBHelper.SetParameter("StuID", id);
dt = DBHelper.ExecQuery();
if (dt.Rows.Count == 0)
return null;
StudentInfoEntiy entity = new StudentInfoEntiy();
entity.StuID = dt.Rows[0]["StuID"].ToString();
entity.StuName = dt.Rows[0]["StuName"].ToString();
entity.StuAge = int.Parse(dt.Rows[0]["StuAge"].ToString());
entity.StuSex = dt.Rows[0]["StuSex"].ToString();
entity.StuHobby = dt.Rows[0]["StuHobby"].ToString();
entity.ProfessionID = int.Parse(dt.Rows[0]["ProfessionID"].ToString());
return entity;
}
#endregion
}
四、业务逻辑层编写
(1)由于业务逻辑层需要使用实体类,所以需要添加实体类的引用。
即在MyBLL项目上右键-->添加-->引用-->项目,在项目中勾选MyEntity项目。
(2)由于业务逻辑层需要调用数据访问层,所以需要添加数据访问层的引用。
即在MyBLL项目上右键-->添加-->引用-->项目,在项目中勾选MyDAL项目。
(3)在MyBLL项目中添加两个类,类代码如下:
ProfessionInfoBLL:
public class ProfessionInfoBLL
{
ProfessionInfoDAL dal = new ProfessionInfoDAL();
#region 新增
public int Add(ProfessionInfoEntity entity)
{
return dal.Add(entity);
}
#endregion
#region 删除
public int Delete(int id)
{
return dal.Delete(id);
}
#endregion
#region 修改
public int Update(ProfessionInfoEntity entity)
{
return dal.Update(entity);
}
#endregion
#region 列表
public List<ProfessionInfoEntity> List()
{
return dal.List();
}
#endregion
#region 详情
public ProfessionInfoEntity Detail(int id)
{
return dal.Detail(id);
}
#endregion
}
StudentInfoBLL:
public class StudentInfoBLL
{
StudentInfoDAL dal = new StudentInfoDAL();
#region 新增
public int Add(StudentInfoEntiy entity)
{
return dal.Add(entity);
}
#endregion
#region 删除
public int Delete(string id)
{
return dal.Delete(id);
}
#endregion
#region 修改
public int Update(StudentInfoEntiy entity)
{
return dal.Update(entity);
}
#endregion
#region 列表
public List<StudentInfoEntiy> List()
{
return dal.List();
}
#endregion
#region 详情
public StudentInfoEntiy Detail(string id)
{
return dal.Detail(id);
}
#endregion
}
五、界面UI层代码编写
(1)由于界面UI层需要使用实体类,所以需要添加实体类的引用。
即在MyUI项目上右键-->添加-->引用-->项目,在项目中勾选MyEntity项目。
(2)由于界面UI层需要调用业务逻辑层,所以需要添加业务逻辑层的引用。
即在MyUI项目上右键-->添加-->引用-->项目,在项目中勾选MyBLL项目。
(1)由于查询学生需要多条件组合查询,所以给数据访问层和业务逻辑层添加条件搜索的方法。
给数据访问层MyDAL中StudentInfoDAL类添加方法:
#region 条件查询
public List<StudentInfoEntiy> Search(StudentInfoEntiy searchObj)
{
string sql = "select * from StudentInfo inner join ProfessionInfo on StudentInfo.ProfessionID = ProfessionInfo.ProfessionID where 1 = 1";
if (searchObj.ProfessionID != 0)
sql += " and StudentInfo.ProfessionID = " + searchObj.ProfessionID;
if (!searchObj.StuName.Equals(""))
sql += " and StuName like '%" + searchObj.StuName + "%'";
DataTable dt = new DataTable();
DBHelper.PrepareSql(sql);
dt = DBHelper.ExecQuery();
List<StudentInfoEntiy> list = new List<StudentInfoEntiy>();
foreach (DataRow item in dt.Rows)
{
StudentInfoEntiy entity = new StudentInfoEntiy();
entity.StuID = item["StuID"].ToString();
entity.StuName = item["StuName"].ToString();
entity.StuAge = int.Parse(item["StuAge"].ToString());
entity.StuSex = item["StuSex"].ToString();
entity.StuHobby = item["StuHobby"].ToString();
entity.ProfessionID = int.Parse(item["ProfessionID"].ToString());
entity.ProfessionName = item["ProfessionName"].ToString();
list.Add(entity);
}
return list;
}
#endregion
给业务逻辑层MyBLL中StudentInfoBLL类添加方法:
#region 条件查询
public List<StudentInfoEntiy> Search(StudentInfoEntiy searchObj)
{
return dal.Search(searchObj);
}
#endregion
(2)在此界面中多个功能需要调用业务逻辑层,定义两个业务逻辑层对象:
ProfessionInfoBLL proBll = new ProfessionInfoBLL();
StudentInfoBLL stuBll = new StudentInfoBLL();
(3)查询窗体绑定专业信息、绑定学生信息以及搜索功能代
#region 绑定下拉框
private void BindPro()
{
List<ProfessionInfoEntity> list = new List<ProfessionInfoEntity>();
list = proBll.List();
list.Insert(0,new ProfessionInfoEntity { ProfessionID=0,ProfessionName="--请选择--"});
this.cmbPro.DataSource = list;
this.cmbPro.DisplayMember = "ProfessionName";
this.cmbPro.ValueMember = "ProfessionID";
}
#endregion
#region 绑定学生数据
private void BindData()
{
StudentInfoEntiy searchObj = new StudentInfoEntiy();
searchObj.ProfessionID = int.Parse(this.cmbPro.SelectedValue.ToString());
searchObj.StuName = this.txtName.Text;
this.dataGridView1.AutoGenerateColumns = false;
this.dataGridView1.DataSource = stuBll.Search(searchObj);
}
#endregion
#region 窗体加载
private void FrmSelect_Load(object sender, EventArgs e)
{
BindPro();
BindData();
}
#endregion
#region 搜索按钮
private void btSearch_Click(object sender, EventArgs e)
{
BindData();
}
#endregion
(4)删除菜单代码:
private void 删除ToolStripMenuItem_Click(object sender, EventArgs e)
{
//添加是否确定删除的对话框
DialogResult result = MessageBox.Show("确定要删除数据吗,删除之后无法恢复!", "提示框",
MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
if (result == DialogResult.Cancel)
return;
string stuid = this.dataGridView1.SelectedRows[0].Cells[0].Value.ToString();
if(stuBll.Delete(stuid) == 1)
MessageBox.Show("删除成功!");
else
MessageBox.Show("删除失败!");
BindData();
}
新增窗体界面及代码:
ProfessionInfoBLL proBll = new ProfessionInfoBLL();
StudentInfoBLL stuBll = new StudentInfoBLL();
#region 绑定下拉框
private void BindPro()
{
List<ProfessionInfoEntity> list = new List<ProfessionInfoEntity>();
list = proBll.List();
list.Insert(0, new ProfessionInfoEntity { ProfessionID = 0, ProfessionName = "--请选择--" });
this.cmbPro.DataSource = list;
this.cmbPro.DisplayMember = "ProfessionName";
this.cmbPro.ValueMember = "ProfessionID";
}
#endregion
private void FrmAdd_Load(object sender, EventArgs e)
{
BindPro();
}
private void btAdd_Click(object sender, EventArgs e)
{
//性别处理
string sex = "";
if (this.rbBoy.Checked == true) sex = this.rbBoy.Text;
if (this.rbGirl.Checked == true) sex = this.rbGirl.Text;
//爱好处理
string hobby = "";
foreach (CheckBox ck in this.panel2.Controls)
{
if (ck.Checked == true)
{
if (!hobby.Equals(""))
hobby += ",";
hobby += ck.Text;
}
}
StudentInfoEntiy entity = new StudentInfoEntiy();
entity.StuID = this.txtId.Text;
entity.StuName = this.txtName.Text;
entity.StuAge = int.Parse(this.txtAge.Text);
entity.StuSex = sex;
entity.StuHobby = hobby;
entity.ProfessionID = int.Parse(this.cmbPro.SelectedValue.ToString());
if (stuBll.Add(entity) == 1)
MessageBox.Show("新增成功!");
else
MessageBox.Show("新增失败!");
this.Close();
}
编辑修改窗体界面及代码:
public string StuID { get; set; } //学生编号
ProfessionInfoBLL proBll = new ProfessionInfoBLL();
StudentInfoBLL stuBll = new StudentInfoBLL();
#region 绑定下拉框
private void BindPro()
{
List<ProfessionInfoEntity> list = new List<ProfessionInfoEntity>();
list = proBll.List();
list.Insert(0, new ProfessionInfoEntity { ProfessionID = 0, ProfessionName = "--请选择--" });
this.cmbPro.DataSource = list;
this.cmbPro.DisplayMember = "ProfessionName";
this.cmbPro.ValueMember = "ProfessionID";
}
#endregion
#region 绑定详情
private void BindDetail()
{
StudentInfoEntiy entity = new StudentInfoEntiy();
entity = stuBll.Detail(this.StuID);
this.txtId.Text = entity.StuID;
this.txtName.Text = entity.StuName;
this.txtAge.Text = entity.StuAge.ToString();
this.cmbPro.SelectedValue = entity.ProfessionID;
//性别处理
if (entity.StuSex.Equals("男"))
this.rbBoy.Checked = true;
else
this.rbGirl.Checked = true;
//爱好处理
string[] arrHobby = entity.StuHobby.Split(',');
foreach (string hobby in arrHobby)
{
foreach (CheckBox ck in this.panel2.Controls)
{
if (ck.Text.Equals(hobby))
ck.Checked = true;
}
}
}
#endregion
private void FrmEdit_Load(object sender, EventArgs e)
{
BindPro();
BindDetail();
}
private void btUpdate_Click(object sender, EventArgs e)
{
//性别处理
string sex = "";
if (this.rbBoy.Checked == true) sex = this.rbBoy.Text;
if (this.rbGirl.Checked == true) sex = this.rbGirl.Text;
//爱好处理
string hobby = "";
foreach (CheckBox ck in this.panel2.Controls)
{
if (ck.Checked == true)
{
if (!hobby.Equals(""))
hobby += ",";
hobby += ck.Text;
}
}
StudentInfoEntiy entity = new StudentInfoEntiy();
entity.StuID = this.txtId.Text;
entity.StuName = this.txtName.Text;
entity.StuAge = int.Parse(this.txtAge.Text);
entity.StuSex = sex;
entity.StuHobby = hobby;
entity.ProfessionID = int.Parse(this.cmbPro.SelectedValue.ToString());
if (stuBll.Update(entity) == 1)
MessageBox.Show("修改成功!");
else
MessageBox.Show("修改失败!");
this.Close();
}
查询窗体中"新增"和"编辑"按钮代码:
private void btAdd_Click(object sender, EventArgs e)
{
FrmAdd frm = new FrmAdd();
frm.Show();
}
private void btEdit_Click(object sender, EventArgs e)
{
string stuid = this.dataGridView1.SelectedRows[0].Cells[0].Value.ToString();
FrmEdit frm = new FrmEdit();
frm.StuID = stuid;
frm.Show();
}