以前没太多关注各多层架构的设计,一直以来都是用的三层架构做网站,最近对N层架构有些兴趣,就稍微研究了一下,由于自己也没怎么用过,也是才学习,中间可能有些错误,请大家指正。
ASP.NET的框架不管有多少层架构,都脱离不了最基本的三层:表现层,业务层,数据访问层。

整个过程的来回都跟请求和数据有关,往右发送请求,往左响应请求返回数据。
最简单的三层结构:Default.aspx——>BLL——>SQLHelper
Default.aspx<——BLL<——SQLHelper 多层架构既然是基于三层架构的,它是对最基础的三层结构进行再抽离,那么就可以在三层架构的基础上再至少抽出一个中间层,是最原始的三层架构之间的桥梁,它起到一个衔接作用。试想一个业务比较复杂的网站,BLL的代码量会比较庞大,基本的三层显然是不够用的。我的做法是在三层的基础之上,先给Web和BLL之间搭一座桥,我叫它BLLManager,它的作用就是来统一管理所有的BLL类;这还不够,我把和SQLHelper相关的数据访问类都放到DAL中(以前的做法是用BLL来访问SQLHelper),这就相当于业务的数据都在DAL中请求和获得;然后我再在BLL和DAL中抽出一个类DALFactory用来管理所有的DAL(除了SQLHelper)。现在基本的结构如下图(不知道我的理解对不对,希望大家指正):
使用N层架构的效果:每一层都可以在仅仅更改很少量的代码后,就能放到物理上不同的服务器上使用,因此结构灵活而且性能更佳。此外,每层做些什么其它层是完全看不到的,因此更改、更新某层,都不再需要重新编译或者更改全部的层了。这是个很强大的功能。例如,如果把数据访问代码与业务逻辑层分离,当数据库服务器更改后,你只需要更改数据访问的代码,因为业务逻辑层是不变的,因此不需要更改或者重新编译业务逻辑层。下面我给了个简单的例子:Default.aspx

Code

<%
@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Test._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="查询" onclick="Button1_Click" />
<div id="infoList" runat="server">
</div>
</form>
</body>
</html>

Default.aspx.cs

Code
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using Model;
using Test.Common;
namespace Test


{
public partial class _Default : System.Web.UI.Page

{
protected void Page_Load(object sender, EventArgs e)

{
}

protected void Button1_Click(object sender, EventArgs e)

{
string name = this.TextBox1.Text;
StringBuilder info = new StringBuilder();
info.Append("<table border='1'>");
info.Append("<tr>");
info.Append("<td>编号</td>");
info.Append("<td>姓名</td>");
info.Append("<td>入职时间</td>");
info.Append("</tr>");
IList<Emp> empList = BLLManager.Instance.EmpBLL.GetEmpList(name);
if (empList!=null)

{
foreach (Emp emp in empList)

{
info.Append("<tr>");
info.Append("<td>"+emp.EmpId+"</td>");
info.Append("<td>"+emp.EmpName+"</td>");
info.Append("<td>" + String.Format("{0:yyyy-MM-dd}", emp.HireDate) + "</td>");
info.Append("</tr>");
}
}
info.Append("</table>");
this.infoList.InnerHtml = info.ToString();
}
}
}

Emp.cs

Code
using System;
using System.Collections.Generic;
using System.Text;
namespace Model
{
public class Emp
{
private int empId;
private string empName;
private DateTime hireDate;
public int EmpId
{
get { return empId; }
set { empId = value; }
}
public string EmpName
{
get { return empName; }
set { empName = value; }
}
public DateTime HireDate
{
get { return hireDate; }
set { hireDate = value; }
}
}
} BLLManager.cs

Code
using System;
using System.Collections.Generic;
using System.Web;
using BLL;
namespace Test.Common


{

/**//// <summary>
/// BLLManager类,统一管理所有的BLL,作为Web层和BLL层的中间层,用来返回BLL类库中某个类的实例
/// 这个类采用单例模式,确保系统中只有一个BLLManager类
/// </summary>
public class BLLManager

{
private static BLLManager _instance;
private BLLManager()

{
}
public static BLLManager Instance

{
get

{
if (_instance == null)

{
lock (typeof(BLLManager))

{
if (_instance == null)

{
_instance = new BLLManager();
}
}
}
return _instance;
}
}
public EmpBLL EmpBLL

{

get
{ return new EmpBLL(); }
}
}
}

EmpBLL.cs

Code
using System;
using System.Collections.Generic;
using System.Text;
using DAL;
using Model;

namespace BLL


{

/**//// <summary>
///
/// </summary>
public class EmpBLL

{
public EmpBLL()

{
DAL = DALFactory.CreateEmpDAL();
}

/**//// <summary>
/// DAL作为EmpBLL的属性,用来get,set对应的EmpDAL类的实例
/// </summary>

public EmpDAL DAL
{ get; set; }
public IList<Emp> GetEmpList(string name)

{
return DAL.GetEmpListByName(name);
}
}
}

DALFactory.cs

Code
using System;
using System.Collections.Generic;
using System.Text;
using Model;
using DAL;
namespace BLL


{

/**//// <summary>
/// DALFactory用来管理所有的DAL类,实例化DAL,所有的DAL类都在这里被实例化
/// </summary>
public class DALFactory

{
public static EmpDAL CreateEmpDAL()

{
return new EmpDAL();
}
}
}

EmpDAL.cs

Code
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using Model;
namespace DAL


{

/**//// <summary>
/// 这一层是数据访问层,和实体类打交道,最终返回的也是一些对象
/// 我的理解是SQLHelper是数据库访问层,是和数据库直接相关的,这应该是两个不同的层次
/// </summary>
public class EmpDAL

{
public IList<Emp> GetEmpListByName(string name)

{
List<Emp> items = null;

SqlParameter[] parms = new SqlParameter[]
{
new SqlParameter("@Name",SqlDbType.VarChar,20)
};
parms[0].Value = name;
//调用存储过程
using (SqlDataReader reader = SQLHelper.ExecuteReader(SQLHelper.CONN_STRING, CommandType.StoredProcedure, "EmpsGetByName", parms))

{
while(reader.Read())

{
//isDBNull()
// 在c#中,进行数据库查询时使用IsDbNull 方法判断数据库中字段值是否存在(注意不是判断是否空值或0) //。将字段所在的行key作为参数传给IsDbNull 方法,此方法对其值进行判断,如果字段值不存在或缺少值则返 //回Ture,如果字段值存在则返回False。
//此方法能方便地对数据库中值的存在与否和是否缺失进行判断,避免了空值的干扰。

int empid = !reader.IsDBNull(0)?reader.GetInt32(0):0;
string empname = !reader.IsDBNull(1)?reader.GetString(1):string.Empty;
DateTime hiredate = !reader.IsDBNull(2)?reader.GetDateTime(2):DateTime.Today;
Emp emp = new Emp();
emp.EmpId = empid;
emp.EmpName = empname;
emp.HireDate = hiredate;
if(items==null)

{
items = new List<Emp>();
}
items.Add(emp);
}
}
return items;
}
}
}

SQLHelper.cs我就不贴了。下面有整个源码(包含sql)的下载 /Files/psunny/N-tier-structure.rar
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?