理解三层架构及其在 C# .NET 中的实现






3.35/5 (18投票s)
什么是三层架构,以及如何实现它。
引言
为了有效地设计软件,我们倾向于采用多层架构。 这种方法已被业界采用,用于编写高效的代码。 在这种编码风格中,变更管理变得非常容易。 让我们简要讨论一下这种方法。 三层流程模型最初有助于我们高效工作。 一段时间后,如果业务逻辑发生任何变化,可以通过在特定层上进行特定更改来轻松完成。 例如,业务逻辑层的变化不会影响另外两层,即数据访问层和 UI。
这三层是如何连接的?
三层架构的数据流图
DAL 的实现
在实现 DAL 之前,您首先需要了解什么是 DAL 或数据访问层,以及它包含什么。
在数据访问层中,数据可以是任何数据库(Oracle、SQL Server、Access、PostgreSQL、MySQL 等)或任何文件。 也就是说,您存储数据的地方。 并且这一层是您的应用程序与该数据之间唯一的连接。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
namespace DAL
// Notice this name of this namespace. In BLL we will use
// this 'DAL' namespace according to the above picture
{
class Database
{
internal string UserName { get; set; }
internal string Password { get; set; }
internal string ServerName { get; set; }
internal string DatabaseName { get; set; }
internal string ConnectionString
{
get { return "Data Source=" + ServerName + ";Initial Catalog=" +
DatabaseName + ";Persist Security Info=True;User ID=" +
UserName + ";Password=" + Password; }
}
/// <summary>
/// Insert, Update and Delete in the database through this method
/// </summary>
/// The SQL Query or the name of the Stored Procedure
/// The values which you have to insert, update, or delete
/// If the first parameter "sql"
/// is any name of the stored procedure then it must be true
/// True for successful execution, otherwise False
public bool InsertUpdateDelete(string sql, Dictionary parameters, bool isProcedure)
{
using (SqlConnection sqlConnection = new SqlConnection(ConnectionString))
{
sqlConnection.Open();
using (SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection))
{
if (isProcedure)
sqlCommand.CommandType =
System.Data.CommandType.StoredProcedure;
else sqlCommand.CommandType = System.Data.CommandType.Text;
// Adding parameters using Dictionary...
foreach (KeyValuePair parameter in parameters)
sqlCommand.Parameters.Add(new SqlParameter(parameter.Key, parameter.Value));
if (sqlCommand.ExecuteNonQuery() > 0) return true;
else return false;
}
}
}
/// <summary>
/// Select from database through this method
/// </summary>
/// The SQL Query or the name of the Stored Procedure
/// If the first parameter "sql" is any name of the stored procedure then it must be true
/// The parameters which you want to pass through WHERE clause in SELECT Query
/// The resultant table aganist the select query or the stored procedure
public DataTable Select(string sql, bool isProcedure, Dictionary parameters = null)
{
using (SqlConnection sqlConnection = new SqlConnection(ConnectionString))
{
sqlConnection.Open();
using (SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection))
{
if (isProcedure) sqlCommand.CommandType = CommandType.StoredProcedure;
else sqlCommand.CommandType = CommandType.Text;
if (parameters != null)
foreach (KeyValuePair parameter in parameters)
sqlCommand.Parameters.Add(new SqlParameter(parameter.Key, parameter.Value));
using (SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand))
{
using (DataTable dt = new DataTable())
{
sqlDataAdapter.Fill(dt);
return dt;
}
}
}
}
}
}
}
BLL 的实现
在实现 BLL 之前,您首先需要了解什么是 BLL 或业务逻辑层,以及它包含什么。
粗略地说,您可以将“表名”视为“类名”,“列名”视为“数据成员或属性的名称”,并将 DDL 和 DML 操作视为该类的方法。
让我们来实现我所说的内容
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using DAL; // Notice this as I've already told you, I'm doing accordingly...
namespace BLL
{
class EmployeeBLL
{
/* This properties are the columns of the table 'Employee' */
public long EmployeeId { get; set; }
public string EmployeeName { get; set; }
public string EmployeeAddress { get; set; }
public double Salary { get; set; }
public DateTime DOJ { get; set; }
/* I've just implement four basic database operations: Insert, Update, Delete, Select */
public bool InsertEmployee()
{
Database db = InitializeDatabase();
string sqlQuery = "INSERT INTO [Employee] ";
sqlQuery += "(EmployeeName, EmployeeAddress, Salary, DOJ) ";
sqlQuery += "VALUES ";
sqlQuery += "(@EmployeeName, @EmployeeAddress, @Salary, @DOJ)";
Dictionary parameters = new Dictionary();
parameters.Add("@EmployeeName", EmployeeName);
parameters.Add("@EmployeeAddress", EmployeeAddress);
parameters.Add("@Salary", Salary);
parameters.Add("@DOJ", DOJ);
return db.InsertUpdateDelete(sqlQuery, parameters, false);
}
public bool UpdateEmployee()
{
Database db = InitializeDatabase();
string sqlQuery = "UPDATE [Employee] SET ";
sqlQuery += "EmployeeName=@EmployeeName, EmployeeAddress=@EmployeeAddress, Salary=@Salary, DOJ=@DOJ ";
sqlQuery += "WHERE EmployeeId=@EmployeeId";
Dictionary parameters = new Dictionary();
parameters.Add("@EmployeeId", EmployeeId);
parameters.Add("@EmployeeName", EmployeeName);
parameters.Add("@EmployeeAddress", EmployeeAddress);
parameters.Add("@Salary", Salary);
parameters.Add("@DOJ", DOJ);
return db.InsertUpdateDelete(sqlQuery, parameters, false);
}
public bool DeleteEmployee()
{
Database db = InitializeDatabase();
string sqlQuery = "DELETE [Employee] WHERE EmployeeId=@EmployeeId";
Dictionary parameters = new Dictionary();
parameters.Add("@EmployeeId", EmployeeId);
return db.InsertUpdateDelete(sqlQuery, parameters, false);
}
public DataTable SelectEmployee()
{
Database db = InitializeDatabase();
string sqlQuery = "SELECT EmployeeId AS Id, EmployeeName AS Name, " +
"EmployeeAddress AS Address, Salary, DOJ AS 'Joining Date' FROM Employee";
return db.Select(sqlQuery, false);
}
public DataRow SelectEmployeeById()
{
Database db = InitializeDatabase();
string sqlQuery = "SELECT EmployeeName AS Name, EmployeeAddress " +
"AS Address, Salary, DOJ AS 'Joining Date' ";
sqlQuery += "FROM Employee WHERE EmployeeId=@EmployeeId";
Dictionary parameters = new Dictionary();
parameters.Add("@EmployeeId", EmployeeId);
return db.Select(sqlQuery, false, parameters).AsEnumerable().First();
}
private Database InitializeDatabase()
{
Database db = new Database();
db.UserName = "sa";
db.Password = "server2008";
db.ServerName = "(local)";
db.DatabaseName = "DebopamDB";
return db;
}
}
}
UI 层的实现
UI 层是用户界面层。 这一层包含基于 Web 的应用程序的 Web 表单及其控件。 以及基于桌面应用程序的 Windows 表单及其控件。
我实现了一个基于桌面的应用程序演示。 在下一个版本中,我将向您展示如何使用我在这里实现的相同 DAL 和 BLL 来实现基于 Web 的应用程序。
主窗体
主窗体后面
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;
using BLL;
// Notice this, I'm using the namespace of BLL, not DAL as described above.
namespace ThreeLayerArchitectureDemo
{
public partial class FormManageEmployee : Form
{
EmployeeBLL employee = new EmployeeBLL();
public FormManageEmployee()
{
InitializeComponent();
}
private void FormManageEmployee_Load(object sender, EventArgs e)
{
FillGridView();
}
// Code aganist 'Insert' button
private void btnInsert_Click(object sender, EventArgs e)
{
using (FormEditUpdateEmployee frmEditUpdateEmp = new FormEditUpdateEmployee())
{
frmEditUpdateEmp.EmployeeId = 0;
if (frmEditUpdateEmp.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
employee = frmEditUpdateEmp.ToEmployee();
if (employee.InsertEmployee())
{
FillGridView();
MessageBox.Show("Successfully Inserted", "Success",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
MessageBox.Show("Error during Inserting", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else return;
}
}
private void FillGridView()
{
grdViewEmployee.DataSource = employee.SelectEmployee();
}
// Code aganist 'Edit' button
private void btnEdit_Click(object sender, EventArgs e)
{
using (FormEditUpdateEmployee frmEditUpdateEmp = new FormEditUpdateEmployee())
{
frmEditUpdateEmp.EmployeeId = long.Parse(grdViewEmployee.CurrentRow.Cells["Id"].Value.ToString());
if (frmEditUpdateEmp.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
employee = frmEditUpdateEmp.ToEmployee();
if (employee.UpdateEmployee())
{
FillGridView();
MessageBox.Show("Successfully Updated",
"Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
MessageBox.Show("Error during Updating",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
return;
}
}
// Code aganist 'Delete' button
private void btnDelete_Click(object sender, EventArgs e)
{
employee.EmployeeId = long.Parse(grdViewEmployee.CurrentRow.Cells["Id"].Value.ToString());
employee.EmployeeName = grdViewEmployee.CurrentRow.Cells["Name"].Value.ToString();
if (MessageBox.Show("Delete " + employee.EmployeeName + "?",
"Delete Employee?", MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
if (employee.DeleteEmployee())
{
FillGridView();
MessageBox.Show("Successfully Deleted",
"Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
MessageBox.Show("Error during Deleting",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
else
return;
}
}
}
插入/编辑窗体
插入/编辑窗体后面
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;
using BLL;
namespace ThreeLayerArchitectureDemo
{
public partial class FormEditUpdateEmployee : Form
{
EmployeeBLL employee = new EmployeeBLL();
public long EmployeeId
{
get { return long.Parse(this.txtName.Tag.ToString()); }
set
{
if (value == 0)
{
this.txtName.Text = null;
this.txtName.Tag = 0;
this.txtAddress.Text = null;
this.txtSalary.Text = null;
this.dateDOJ.Value = DateTime.Today.Date;
}
else
{
employee.EmployeeId = value;
DataRow dr = employee.SelectEmployeeById();
this.txtName.Text = dr["Name"].ToString();
this.txtName.Tag = value;
this.txtAddress.Text = dr["Address"].ToString();
this.txtSalary.Text = dr["Salary"].ToString();
this.dateDOJ.Value = DateTime.Parse(dr["Joining Date"].ToString());
}
}
}
internal EmployeeBLL ToEmployee()
{
employee.EmployeeId = long.Parse(this.txtName.Tag.ToString());
employee.EmployeeName = this.txtName.Text;
employee.EmployeeAddress = this.txtAddress.Text;
employee.Salary = double.Parse(this.txtSalary.Text);
employee.DOJ = this.dateDOJ.Value;
return employee;
}
public FormEditUpdateEmployee()
{
InitializeComponent();
}
// Code aganist 'Save' button
private void btnSave_Click(object sender, EventArgs e)
{
this.DialogResult = System.Windows.Forms.DialogResult.OK;
this.Close();
}
// Code aganist 'Cancel' button
private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Close();
}
}
}
使用三层架构的优点
- 可扩展性:应用程序服务器可以部署在许多机器上,数据库不再需要来自每个客户端的连接。
- 可重用性:您可以使用不同的用户界面(如 ASP.NET Web Form、ASP.NET Web Service、.NET Window Form 等)来重用业务逻辑层 (BLL),即中间层。 您可以重用数据库访问层 (DAL),即具有不同项目的顶层。
- 改进数据完整性:BLL(即中间层)可以确保只允许在数据库中插入、更新或删除有效数据。
- 提高安全性:由于客户端无法直接访问数据库。 BLL 通常更安全,因为它位于更安全的中央服务器上。
- 减少分发:对 BLL/DLL 的更改只需在应用程序服务器上更新,不必分发给所有客户端。
- 隐藏数据库结构:由于数据库的实际结构对用户隐藏。
- 提高可用性
使用三层架构的缺点
- 增加开发人员的复杂性和工作量
- 一般来说,与两层架构相比,三层架构构建起来更复杂
- 通信点加倍
关注点
当您实现此代码时,您还将学习在 C# 中使用Dictionary
、using
和 internal
。历史
- 版本 1.0:2013 年 11 月。