65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.35/5 (18投票s)

2013年11月7日

CPOL

3分钟阅读

viewsIcon

66029

downloadIcon

1636

什么是三层架构,以及如何实现它。

引言

为了有效地设计软件,我们倾向于采用多层架构。 这种方法已被业界采用,用于编写高效的代码。 在这种编码风格中,变更管理变得非常容易。 让我们简要讨论一下这种方法。 三层流程模型最初有助于我们高效工作。 一段时间后,如果业务逻辑发生任何变化,可以通过在特定层上进行特定更改来轻松完成。 例如,业务逻辑层的变化不会影响另外两层,即数据访问层和 UI。

这三层是如何连接的?

Connection between three layers

三层架构的数据流图

DFD of Three Layer Architecture

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 的应用程序。

主窗体

The Main Window Form

主窗体后面

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;
        }
    }
}

插入/编辑窗体

Form for Insert or Edit

插入/编辑窗体后面

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# 中使用 Dictionaryusinginternal

历史

  • 版本 1.0:2013 年 11 月。
© . All rights reserved.