通常意义上的三层架构就是将整个业务应用划分为:
表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。
区分层次的目的即为了“高内聚,低耦合”的思想。
表现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候的所见所得。
业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理。
数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增添、删除、修改、更新、查找等每层之间是一种垂直的关系。
优点: 分工明确,条理清晰,易于调试,而且具有可扩展性。
缺点: 增加成本。
DAL、BLL、UI、Model(模型层)、Common(通用层)
调用关系:
DAL层调用Model层,Common层
BLL层调用DAL层、Model层、Common层
UI层调用BLL层、Model层、Common层
例如影院系统零食销售示例中,我创建了如上图介绍的五个类库(其中COMMON可以不创建,例子中没有用到,其余四个类库内容在下文展开详细叙述)
开发顺序:DAL->BLL->UI 翻过来也可以
(用的SQL Server)首先创建一个叫 movie 的数据库,接着创建Snacks(零食)和SnacksType(零食分类)两张表。
上代码哈哈哈(也可以手动创建):
USE [movie]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Snacks](
[SId] [int] IDENTITY(1,1) NOT NULL,
[STitle] [varchar](50) NOT NULL,
[SPrice] [decimal](5, 2) NOT NULL,
[STypeId] [int] NOT NULL,
[SIsDelete] [int] NOT NULL,
CONSTRAINT [PK_Snacks] PRIMARY KEY CLUSTERED
(
[SId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[SnacksType](
[SId] [int] IDENTITY(1,1) NOT NULL,
[STitle] [varchar](10) NOT NULL,
[SIsDelete] [int] NOT NULL
) ON [PRIMARY]
GO
添加数据:
SET IDENTITY_INSERT [dbo].[Snacks] ON
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (1, N'可比克', CAST(15.00 AS Decimal(5, 2)), 1, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (2, N'手指饼', CAST(7.50 AS Decimal(5, 2)), 1, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (3, N'番茄鸡味块', CAST(7.50 AS Decimal(5, 2)), 1, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (4, N'可乐', CAST(4.00 AS Decimal(5, 2)), 2, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (5, N'雪碧', CAST(4.00 AS Decimal(5, 2)), 2, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (6, N'橙汁', CAST(8.00 AS Decimal(5, 2)), 2, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (7, N'西瓜汁', CAST(8.00 AS Decimal(5, 2)), 2, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (8, N'奶茶', CAST(8.00 AS Decimal(5, 2)), 2, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (9, N'爆米花中份', CAST(10.00 AS Decimal(5, 2)), 3, 0)
INSERT [dbo].[Snacks] ([SId], [STitle], [SPrice], [STypeId], [SIsDelete]) VALUES (10, N'爆米花大份', CAST(15.00 AS Decimal(5, 2)), 3, 0)
SET IDENTITY_INSERT [dbo].[Snacks] OFF
SET IDENTITY_INSERT [dbo].[SnacksType] ON
INSERT [dbo].[SnacksType] ([SId], [STitle], [SIsDelete]) VALUES (1, N'零食', 0)
INSERT [dbo].[SnacksType] ([SId], [STitle], [SIsDelete]) VALUES (2, N'饮料', 0)
SET IDENTITY_INSERT [dbo].[SnacksType] OFF
零食类 Snacks:
namespace MODEL
{
public class Snacks
{
//--[SId], [STitle], [SPrice], [STypeId], [SIsDelete]
public string SId {
get; set; }
public string STitle {
get; set; }
public string SPrice {
get; set; }
public string STypeId {
get; set; }
public string SIsDelete {
get; set; }
public string STypeTitle {
get; set; }
public Snacks() {
}
}
}
零食分类类 SnacksType:
namespace MODEL
{
public class SnacksType
{
//--[SId], [STitle], [SIsDelete]
public string SId {
get; set; }
public string STitle {
get; set; }
public string SIsDelete {
get; set; }
public SnacksType() {
}
}
}
数据访问层里加了一个类SqlHelper,帮助操作数据库。
SqlHelper:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
namespace DAL
{
public class SqlHelper
{
private static string connStr = "Data Source=.;Initial Catalog=movie;Integrated Security=True";
//增、删、改
///
/// 执行增、删、改 的sql语句,返回受影响的行数
///
/// 要执行的命令
/// 命令中的参数
/// 受影响的行数
public static int ExecuteNonQuery(string sql, SqlParameter[] ps)
{
using (SqlConnection conn = new SqlConnection(connStr))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
conn.Open();
cmd.Parameters.AddRange(ps);
return cmd.ExecuteNonQuery();
}
}
}
///
/// 执行增、删、改 的sql语句,返回受影响的行数
///
/// 要执行的命令
/// 命令中的参数
/// 受影响的行数
public static int ExecuteNonQuery(string sql)
{
using (SqlConnection conn = new SqlConnection(connStr))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
conn.Open();
return cmd.ExecuteNonQuery();
}
}
}
//查询
public static DataTable getDataTable(string sql, SqlParameter[] ps)
{
using (SqlConnection conn = new SqlConnection(connStr))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
cmd.Parameters.AddRange(ps);
sda.Fill(dt);
return dt;
}
}
}
}
public static DataTable getDataTable(string sql)
{
using (SqlConnection conn = new SqlConnection(connStr))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
sda.Fill(dt);
return dt;
}
}
}
}
}
}
零食 SnacksDAL:
using MODEL;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
namespace DAL
{
public class SnacksDAL
{
//删除
public int delete(string sid)
{
string sql = "update Snacks set SIsDelete=1 where SId=@SId";
SqlParameter[] ps =
{
new SqlParameter("@SId",sid)
};
return SqlHelper.ExecuteNonQuery(sql, ps);
}
//修改
public int update(Snacks s)
{
string sql = "update Snacks set STitle=@STitle,Sprice=@Sprice,STypeId=@STypeId where SId=@SId";
SqlParameter[] ps =
{
new SqlParameter("@Stitle",s.STitle),
new SqlParameter("@Sprice",s.SPrice),
new SqlParameter("@STypeId",s.STypeId),
new SqlParameter("@SId",s.SId)
};
return SqlHelper.ExecuteNonQuery(sql, ps);
}
//新增
public int add(Snacks s)
{
string sql = "insert into Snacks(STitle,Sprice,STypeId)values(@STitle,@Sprice,@STypeId)";
SqlParameter[] ps =
{
new SqlParameter("@STitle",s.STitle),
new SqlParameter("@Sprice",s.SPrice),
new SqlParameter("@STypeId",s.STypeId)
};
return SqlHelper.ExecuteNonQuery(sql, ps);
}
//查询显示
public DataTable getList(Dictionary<string, string> dic)
{
List<SqlParameter> paraList = new List<SqlParameter>();
string sql = "select s.SId,s.STitle,st.STitle STypeTitle,SPrice from Snacks s,SnacksType st where s.STypeId=st.SId and s.SIsDelete=0";
if (dic.Count > 0)
{
foreach (KeyValuePair<string, string> kv in dic)
{
sql += " and s." + kv.Key + " like @" + kv.Key;
string pname = "@" + kv.Key;
string pvalue = "%" + kv.Value + "%";
paraList.Add(new SqlParameter(pname, pvalue));
}
}
return SqlHelper.getDataTable(sql, paraList.ToArray());
}
}
}
零食分类 SnacksTypeDAL:
using MODEL;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
namespace DAL
{
public class SnacksTypeDAL
{
//删除
public int delete(string sid)
{
string sql = "delete from SnacksType where SId=@SId";
SqlParameter[] ps =
{
new SqlParameter("@SId",sid)
};
return SqlHelper.ExecuteNonQuery(sql, ps);
}
//修改
public int update(SnacksType s)
{
string sql = "update SnacksType set STitle=@STitle where SId=@SId";
SqlParameter[] ps =
{
new SqlParameter("@STitle",s.STitle),
new SqlParameter("@SId",s.SId)
};
return SqlHelper.ExecuteNonQuery(sql, ps);
}
//添加
public int add(SnacksType s)
{
string sql = "insert into SnacksType(STitle)values(@STitle)";
SqlParameter[] ps =
{
new SqlParameter("@STitle",s.STitle)
};
return SqlHelper.ExecuteNonQuery(sql, ps);
}
//查询显示
public DataTable getList()
{
string sql = "select SId,STitle from SnacksType";
return SqlHelper.getDataTable(sql);
}
}
}
零食 SnacksBLL:
using DAL;
using MODEL;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
namespace BLL
{
public class SnacksBLL
{
SnacksDAL dal = new SnacksDAL();
//删除
public bool delete(string sid)
{
return dal.delete(sid) > 0;
}
//修改
public bool update(Snacks s)
{
return dal.update(s) > 0;
}
//添加
public bool add(Snacks s)
{
return dal.add(s) > 0;
}
//查询显示
public List<Snacks> getList(Dictionary<string, string> dic)
{
List<Snacks> list = new List<Snacks>();
DataTable dt = dal.getList(dic);
foreach (DataRow row in dt.Rows)
{
Snacks s = new Snacks();
s.SId = row["SId"].ToString();
s.STitle = row["STitle"].ToString();
s.SPrice = row["SPrice"].ToString();
s.STypeId = row["STypeTitle"].ToString();
list.Add(s);
}
return list;
}
}
}
零食分类 SnacksTypeBLL:
using DAL;
using MODEL;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
namespace BLL
{
public class SnacksTypeBLL
{
SnacksTypeDAL dal = new SnacksTypeDAL();
//删除
public bool delete(string sid)
{
return dal.delete(sid) > 0;
}
//修改
public bool update(SnacksType s)
{
return dal.update(s) > 0;
}
//添加
public bool add(SnacksType s)
{
return dal.add(s) > 0;
}
//查询显示
public List<SnacksType> getList()
{
List<SnacksType> list = new List<SnacksType>();
DataTable dt = dal.getList();
foreach (DataRow row in dt.Rows)
{
SnacksType t = new SnacksType();
t.SId = row["SId"].ToString();
t.STitle = row["STitle"].ToString();
list.Add(t);
}
return list;
}
}
}
using BLL;
using MODEL;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace UI
{
public partial class FormSnacks : Form
{
public FormSnacks()
{
InitializeComponent();
}
SnacksBLL bll = new SnacksBLL();
//拼接模糊查询条件
private void loadList()
{
Dictionary<string, string> dic = new Dictionary<string, string>();
string titleSearch = txtTitleSearch.Text.Trim();
string typesearch = ddlTypeSearch.SelectedValue.ToString();
//MessageBox.Show(typesearch);
//创建泛型键值对集合、用于存储SQL语句中模糊查询的条件
if (txtTitleSearch.Text != "")
{
dic.Add("STitle", txtTitleSearch.Text);
}
if (typesearch != "0")
{
dic.Add("STypeId", typesearch);
}
dgvList.AutoGenerateColumns = false;
dgvList.DataSource = bll.getList(dic);
}
//分类搜索下拉框
private void loadSearchTypeList()
{
SnacksTypeBLL stbll = new SnacksTypeBLL();
List<SnacksType> list = stbll.getList();
SnacksType st = new SnacksType();
st.SId = "0";
st.STitle = "全部";
// list.Add(st);
list.Insert(0, st);
//绑定数据
ddlTypeSearch.DisplayMember = "STitle";
ddlTypeSearch.ValueMember = "SId";
ddlTypeSearch.DataSource = list;
}
//增加/修改分类下拉框
private void loadAddTypeList()
{
SnacksTypeBLL stbll = new SnacksTypeBLL();
List<SnacksType> list = stbll.getList();
SnacksType st = new SnacksType();
st.SId = "0";
st.STitle = "全部";
// list.Add(st);
list.Insert(0, st);
//SId,STitle
ddlTypeAdd.DisplayMember = "STitle";
ddlTypeAdd.ValueMember = "SId";
ddlTypeAdd.DataSource = list;
}
private void FormSnacks_Load(object sender, EventArgs e)
{
loadSearchTypeList();
loadAddTypeList();
loadList();
}
private void DgvList_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
DataGridViewRow rows = dgvList.SelectedRows[0];
txtId.Text = rows.Cells["SId"].Value.ToString();
txtTitleSave.Text = rows.Cells["STitle"].Value.ToString();
txtPrice.Text = rows.Cells["SPrice"].Value.ToString();
txtId.Text = rows.Cells["SId"].Value.ToString();
ddlTypeAdd.Text = rows.Cells["STypeId"].Value.ToString();
btnSave.Text = "修改";
}
private void BtnSave_Click(object sender, EventArgs e)
{
string str = txtId.Text.Trim();
Snacks s = new Snacks();
s.SId = txtId.Text;
s.STitle = txtTitleSave.Text;
s.SPrice = txtPrice.Text;
s.STypeId = ddlTypeAdd.SelectedValue.ToString();
s.STypeTitle = ddlTypeAdd.Text;
if (str.Equals("添加时无编号"))
{
if (bll.add(s))
{
MessageBox.Show("添加成功!");
loadList();
}
else
{
MessageBox.Show("添加失败!");
}
}
else
{
if (bll.update(s))
{
MessageBox.Show("修改成功!");
loadList();
}
else
{
MessageBox.Show("修改失败!");
}
}
}
private void BtnCancel_Click(object sender, EventArgs e)
{
txtId.Text = "添加时无编号";
txtTitleSave.Text = "";
txtPrice.Text = "";
ddlTypeAdd.SelectedIndex = 0;
btnSave.Text = "添加";
}
private void BtnRemove_Click(object sender, EventArgs e)
{
DataGridViewRow rows = dgvList.SelectedRows[0];
string sid = rows.Cells["SId"].Value.ToString();
if (bll.delete(sid))
{
MessageBox.Show("删除成功!");
loadList();
}
else
{
MessageBox.Show("删除失败!");
}
}
private void TxtTitleSearch_TextChanged(object sender, EventArgs e)
{
loadList();
}
private void DdlTypeSearch_SelectedIndexChanged(object sender, EventArgs e)
{
loadList();
}
private void BtnSearchAll_Click(object sender, EventArgs e)
{
txtTitleSearch.Clear();
ddlTypeSearch.Text = "全部";
}
private void BtnAddType_Click(object sender, EventArgs e)
{
FormSnacksType fst = new FormSnacksType();
fst.StartPosition = FormStartPosition.CenterScreen;
if (fst.ShowDialog() == DialogResult.Yes)
{
loadAddTypeList();
loadSearchTypeList();
}
}
}
}
点击首页的“分类管理“按钮会打开零食分类页面(这里我给这个按钮起名叫btnAddType,,命名不严谨了),其实零食分类管理和零食管理的原理都一样都是增删查改。
零食分类界面:
事件代码:
using BLL;
using MODEL;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace UI
{
public partial class FormSnacksType : Form
{
public FormSnacksType()
{
InitializeComponent();
}
SnacksTypeBLL bll = new SnacksTypeBLL();
private DialogResult dr = DialogResult.No;
//刷新
private void loadList()
{
List<SnacksType> list = bll.getList();
dgvList.AutoGenerateColumns = false;
dgvList.DataSource = list;
}
private void FormSnacksType_Load(object sender, EventArgs e)
{
loadList();
}
private void BtnSave_Click(object sender, EventArgs e)
{
string str = txtId.Text.Trim();
if (str.Equals("添加时无编号"))
{
SnacksType s = new SnacksType();
s.STitle = txtTitle.Text;
if (bll.add(s))
{
MessageBox.Show("添加成功!");
loadList();
dr = DialogResult.Yes;
}
else
{
MessageBox.Show("添加失败!");
}
}
else
{
SnacksType s = new SnacksType();
s.SId = txtId.Text;
s.STitle = txtTitle.Text;
if (bll.update(s))
{
MessageBox.Show("修改成功!");
loadList();
dr = DialogResult.Yes;
}
else
{
MessageBox.Show("修改失败!");
}
}
}
private void DgvList_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
DataGridViewRow rows = dgvList.SelectedRows[0];
txtId.Text = rows.Cells["SId"].Value.ToString();
txtTitle.Text = rows.Cells["STitle"].Value.ToString();
btnSave.Text = "修改";
}
private void BtnCancel_Click(object sender, EventArgs e)
{
txtId.Text = "添加时无编号";
txtTitle.Text = "";
btnSave.Text = "添加";
}
private void BtnRemove_Click(object sender, EventArgs e)
{
DataGridViewRow row = dgvList.SelectedRows[0];
string sid = row.Cells["SId"].Value.ToString();
if (bll.delete(sid))
{
MessageBox.Show("删除成功!");
loadList();
dr = DialogResult.Yes;
}
else
{
MessageBox.Show("删除失败!");
}
}
private void FormSnacksType_FormClosing(object sender, FormClosingEventArgs e)
{
this.DialogResult = dr;
}
}
}
运行要注意把Program中的运行窗体改为我们需要的,比如这里是FormSnacks();
数据显示ok:
分类查找模糊查找ok:
添加数据就直接在右侧输入相关信息,点击添加就ok了;
删除数据选中数据所在行,点击删除按钮就ok了;
修改是双击修改信息所在行,该信息就会被读取输出在右侧修改栏(此时右侧添加按钮变为修改按钮),在输出的数据上编辑为修改后数据,点击修改即可完成:
点击分类管理,会打开零食分类界面,该页面的操作原理同零食管理界面: