PetShop的三层架构介绍.03
实际项目架构
如下是一个实际项目架构图形例子:
图形符号含义,实线+空心三角:代表泛化(据说泛化包含继承和实现)。
虚线+空心三角:代表实现,这里我更愿意用"实现符号"代替"泛化符号",下图是我设计时忘了把实线转换成虚线了。
虚线+箭头:代表依赖。
.从UI层开始
一个设定会员号密码的例子:
UserPwd.aspx.cs(UI层调用BLL层)
......
using ReflectionFactory;
using IBLL;
using Model;
using System.Collections.Generic;
public partial class AdminClient_UserPwd : System.Web.UI.Page
{
private static CompanyInfo companyInfo = new CompanyInfo();//实例化实体类对象
private static UserInfo userInfo = new UserInfo();//实例化实体类对象UserInfo
//引用一个企业服务接口,在这里反射工厂(也就是IoC容器)会把企业服务类的实例化对象
//初始化给该企业服务接口。
private static readonly ICompanyBLL companyBLL = BLLFactory.CreateCompanyBLL();
//同上,这里注入的是一个会员服务类的具体实现。
private static readonly IUserBLL userBLL = BLLFactory.CreateUserBLL();
protected void Page_Load(object sender, EventArgs e){...}
protected void btn_ResetClick(object sender, EventArgs e)
{//调用企业服务companyBLL的具体方法、会员服务userBLL的具体方法
if (string.IsNullOrEmpty(this.txt_NewPwd.Value)) return;
//按CoID找出 会员号
companyInfo.CoID = Convert.ToInt32(Request.QueryString["CompanyID"]);
IList<CompanyInfo> companyIL = companyBLL.GetCompany1(companyInfo);
userInfo.UserAccount = companyIL[0].CoUserAccount;
companyIL.Clear();
//更新该会员号的密码
userInfo.UserPwd = this.txt_NewPwd.Value;
if (userBLL.UpdateUserPwd(userInfo))
{
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "",
"alert('修改成功!');window.location.href=location.href;", true); return;
}
}
}
.上面例子引出了反射工厂机制
1.BLLFactory.cs和DALFactory.cs
BLLFactory.cs:BLL层各个类的反射注入工厂
...
using IBLL;
namespace ReflectionFactory
{
/// <summary>业务逻辑层工厂,用于获取相应的业务逻辑层对象</summary>
public sealed class BLLFactory
{
/// <summary>
/// 获取会员模块 逻辑业务层对象
/// </summary>
public static IUserBLL CreateUserBLL()
{
return (IUserBLL)DependencyInjector.GetBLLObject("UserBLL");
}
//更多模块...
...
}
}
DALFactory.cs:DAL层各个类的反射注入工厂
...
using IDAL;
namespace ReflectionFactory
{
///<summary>数据访问层工厂,用于获取相应的数据访问层对象</summary>
public sealed class DALFactory
{
/// <summary>
/// 获取会员模块 数据访问层对象
/// </summary>
/// <returns></returns>
public static IUserDAL CreateUserDAL()
{
return (IUserDAL)DependencyInjector.GetDALObject("UserDAL");
}
//更多模块...
...
}
}
2.反射机制=反射+Web.config
BLLFactory.cs和DALFactory.cs调用DependencyInjector.cs
DependencyInjector.cs:依赖注入的提供者,使用反射机制实现

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Configuration;
using System.Reflection;
using Common;
namespace ReflectionFactory
{
/// <summary>
/// 依赖注入提供者
/// 使用反射机制实现
/// </summary>
public sealed class DependencyInjector
{
/// <summary>
/// 取得数据访问层对象
/// 首先检查缓存中是否存在,如果不存在,则利用反射机制返回对象
/// </summary>
/// <param name="className">数据访问类名称</param>
/// <returns>数据访问层对象</returns>
public static object GetDALObject(string className)
{
///取得数据访问层名称,首先检查缓存,不存在则到配置文件中读取
///缓存依赖项为Web.config文件
object dal = CacheAccess.GetFromCache("DAL");
if (dal == null)
{
CacheDependency fileDependency = new CacheDependency(
HttpContext.Current.Server.MapPath("Web.config"));
dal = ConfigurationManager.AppSettings["DAL"];
CacheAccess.SaveToCache("DAL", dal, fileDependency);
}
///取得数据访问层对象
string dalName = (string)dal;
string fullClassName = dalName + "." + className;
object dalObject = CacheAccess.GetFromCache(className);
if (dalObject == null)
{
CacheDependency fileDependency = new CacheDependency(
HttpContext.Current.Server.MapPath("Web.config"));
dalObject = Assembly.Load(dalName).CreateInstance(fullClassName);
CacheAccess.SaveToCache(className, dalObject, fileDependency);
}
return dalObject;
}
/// <summary>
/// 取得业务逻辑层对象
/// 首先检查缓存中是否存在,如果不存在,则利用反射机制返回对象
/// </summary>
/// <param name="className">业务逻辑类名称</param>
/// <returns>业务逻辑层对象</returns>
public static object GetBLLObject(string className)
{
///取得业务逻辑层名称,首先检查缓存,不存在则到配置文件中读取
///缓存依赖项为Web.config文件
object bll = CacheAccess.GetFromCache("BLL");
if (bll == null)
{
CacheDependency fileDependency = new CacheDependency(
HttpContext.Current.Server.MapPath("Web.config"));
bll = ConfigurationManager.AppSettings["BLL"];
CacheAccess.SaveToCache("BLL", bll, fileDependency);
}
///取得业务逻辑层对象
string bllName = (string)bll;
string fullClassName = bllName + "." + className;
object bllObject = CacheAccess.GetFromCache(className);
if (bllObject == null)
{
CacheDependency fileDependency = new CacheDependency(
HttpContext.Current.Server.MapPath("Web.config"));
bllObject = Assembly.Load(bllName).CreateInstance(fullClassName);
CacheAccess.SaveToCache(className, bllObject, fileDependency);
}
return bllObject;
}
}
}
Web.config:UI层配置文件
<configuration>
<appSettings>
<add key="DAL" value="DAL.SOL"/>
<add key="BLL" value="BLL.SOL"/>
</appSettings>
<connectionStrings>
3.DependencyInjector.cs调用到Common辅助类模块中的CacheAccess.cs类

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.Caching;
namespace Common
{
/// <summary>
/// 辅助类,用于缓存操作
/// </summary>
public sealed class CacheAccess
{
/// <summary>
/// 将对象加入到缓存中
/// </summary>
/// <param name="cacheKey">缓存键</param>
/// <param name="cacheObject">缓存对象</param>
/// <param name="dependency">缓存依赖项</param>
public static void SaveToCache(string cacheKey, object cacheObject,
CacheDependency dependency)
{
Cache cache = HttpRuntime.Cache;
cache.Insert(cacheKey, cacheObject, dependency);
}
/// <summary>
/// 从缓存中取得对象,不存在则返回null
/// </summary>
/// <param name="cacheKey">缓存键</param>
/// <returns>获取的缓存对象</returns>
public static object GetFromCache(string cacheKey)
{
Cache cache = HttpRuntime.Cache;
return cache[cacheKey];
}
}
}
.讲述BLL层的故事
BLL业务逻辑层模块如何"实现"IBLL接口族模块,且"调用DAL数据访问层模块"?
UserBLL.cs:BLL层的会员类
...
using ReflectionFactory;
using IBLL;
using Model;
using IDAL;
using Common;
namespace BLL
{
public class UserBLL:IUserBLL
{
private static readonly IUserDAL userDAL = DALFactory.CreateUserDAL();
/// <summary>
/// Tb_User更新会员密码
/// </summary>
public bool UpdateUserPwd(UserInfo user)
{
return userDAL.UpdateUserPwd(user);
}
//更多方法...
}
}
IUserBLL.cs:BLL层的会员类接口
...
using Model;
namespace IBLL
{
public interface IUserBLL
{
/// <summary>
/// Tb_User更新会员密码
/// </summary>
bool UpdateUserPwd(UserInfo user);
//更多方法...
}
}
IUserDAL.cs:DAL层的会员类接口
...
using Model;
namespace IDAL
{
public interface IUserDAL
{
/// <summary>
/// Tb_User更新会员密码
/// </summary>
bool UpdateUserPwd(UserInfo user);
//更多方法...
}
}
.DAL层的精彩

...
using System.Data;
using System.Data.SqlClient;
using IDAL;
using DBUtility;
using Model;
using Common;
namespace DAL
{
public class UserDAL : IUserDAL
{
private const string SQL_UPDATE_PASSWORD = "UPDATE Tb_User SET UserPwd=@UserPwd WHERE UserAccount=@UserAccount;";
//更多T-SQL语句...
private const string PARM_UserPwd = "@UserPwd";
private const string PARM_UserAccount = "@UserAccount";
//更多参数化查询参数...
/// <summary>
/// Tb_User更新会员密码
/// </summary>
public bool UpdateUserPwd(UserInfo user)
{
SqlParameter[] parms = new SqlParameter[]{
new SqlParameter(PARM_UserPwd,SqlDbType.VarChar,50),
new SqlParameter(PARM_UserAccount,SqlDbType.VarChar,50)
};
parms[0].Value = user.UserPwd;
parms[1].Value = user.UserAccount;
if (SqlHelper.ExecuteNonQuery(SqlHelper.connString, CommandType.Text, SQL_UPDATE_PASSWORD, parms) > 0)
{
return true;
}
else { return false; }
}
//更多方法...
}
}
UserDAL.cs类涉及到"参数化查询"和对"DBUtility类"的调用,DBUtility模块主要就是微软的那个SQLHelper.cs,请自行上网搜索下载。
这里简述一下参数化查询:
private const string SQL_UPDATE_PASSWORD =
"UPDATE Tb_User SET UserPwd=@UserPwd WHERE UserAccount=@UserAccount;";
@UserPwd、@UserAccount对应T-SQL语句中的变量;
private const string PARM_UserPwd = "@UserPwd";
private const string PARM_UserAccount = "@UserAccount";
PARM_UserPwd、PARM_UserAccount是C#中的变量,对应于T-SQL语句中的@UserPwd、@UserAccount;
在程序中是把变量值传递给@UserPwd、@UserAccount的媒介。
/// <summary>
/// Tb_User更新会员密码
/// </summary>
public bool UpdateUserPwd(UserInfo user)
{
SqlParameter[] parms = new SqlParameter[]{
new SqlParameter(PARM_UserPwd,SqlDbType.VarChar,50),
new SqlParameter(PARM_UserAccount,SqlDbType.VarChar,50)
};
parms[0].Value = user.UserPwd;
parms[1].Value = user.UserAccount;
if (SqlHelper.ExecuteNonQuery(SqlHelper.connString,
CommandType.Text, SQL_UPDATE_PASSWORD, parms) > 0)
{
return true;
}
else { return false; }
}
什么是参数化查询?就是把存储过程以T-SQL语句的文本形式执行。
优点有其二(个人已知的,未知的不探讨):防止T-SQL语句注入攻击;存储过程存在调试难,维护难的情况,参数化查询相对小型、灵活。
PetShop的三层架构主要篇章到此结束,根据需要会对其中涉及到的一些知识进行补篇。
作者:Star Eyes
欢迎转载,转载请注明:Star-Studio[http://star-studio.cnblogs.com]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)