掌上租项目是如鹏 .NET 课程中的一个实战项目,架构搭建使用的是 Fluent API,实体都要手动去编写,总觉得很麻烦,结合所学的知识,重新搭建项目架构,设计好数据库,通过 ADO.NET 实体数据模型 + T4 ,自动生成各层基础代码。
项目源码:https://gitee.com/linyq-gitee/ZSZDemo
项目架构示意图:
项目架构总结:
一、使用 PowerDesigner 设计数据库,生成sql脚本
二、执行 sql 创建数据库
三、Model 层
通过 EF Database First 创建
数据很重要,数据删除都是进行软删除
问题:BaseDal 中怎么进行软删除?
解决方案1:BaseDal 中对实体进行软删除
解决方案2:所有实体必须包含软删除字段,定义实体基类,所有实体都继承这个基类
namespace ZSZ.Model { /// <summary>
/// 约定大于配置 /// </summary>
public abstract class BaseEntity { public long Id { get; set; } public bool IsDeleted { get; set; } public System.DateTime CreateDateTime { get; set; } } }
BaseDal 软删除
public void Deleted(T entity) { ctx.Entry(entity).State = EntityState.Deleted; // ctx.SaveChanges();
}
四、DAL 层
问题:Dal 类中都有基础的 CRUD 代码
解决方案:Don't Repeat Yourself ! 不要重复你自己
抽取基础代码定义基类 BaseDal
解耦第一步:接口
解耦第二步:工厂模式解决 new 的问题
问题:多个实体数据模型切换
解决方案:通过简单工厂 DbContextFactory 创建实例
public partial class BaseDal<T> where T :BaseEntity { //DbContext ctx = new ZSZEntities();
/// <summary>
/// 多个实体数据模型切换 /// </summary>
public DbContext ctx { get { return DbContextFactory.GetCurrentDbContext(); } } }
普通 Dal 类定义
public partial class UserDal:BaseDal<User>, IUserDal { }
五、IDAL 层
问题:
1.跨数据库 SQL Server 、MySQL
2.数据访问驱动层切换 EFDAL、NHDAL
解决方案:依赖接口编程,通过接口方式解耦
基接口:IBaseDal
普通 Dal 接口定义:
public partial class UserDal:BaseDal<User>, IUserDal { }
IDbSession 接口 (解耦 DALFactory 层 中的 DbSession )
六、DALFactory 层
问题:数据访问层可能发生变化
IUserDal = new EFDAL.UserDal(); //IUserDal = new NHDAL.UserDal();
解决方案1:简单工厂模式
public partial class StaticDalFactory { public static IUserDal GetUserDal() { return EFDAL.UserDal(); } }
缺点:切换时需要大量替换
解决方案2:反射+抽象工厂
约定大于配置
EFDAL.UserDal()
NHDAL.UserDal()
Web.config
<appSettings>
<!--抽象工厂创建数据库访问层实例所在的程序集名称-->
<add key="DalAssemblyName" value="ZSZDemo.EFDAL" />
</appSettings>
public partial class StaticDalFactory { public static string assemblyName = ConfigurationManager.AppSettings["DalAssemblyName"]; public static IUserDal GetUserDal() { return Assembly.Load(assemblyName).CreateInstance(assemblyName + ".UserDal") as IUserInfoDal; } }
解决方案3:IOC\DI 依赖注入
Spring.NET、AutoFac
new 一个 class 的时候,.NET 会现在内存堆中开辟一块用来存放实例的空间,然后再对实例进行初始化,而开辟内存堆空间的时候,这些空间并不是连续的,所以当一个项目中大量地使用了 new 的时候,就会造成内存堆中存在大量地碎片,这种想象会对系统造成故障。
解决方案:构建 DbSession , DbSession 把所有实体对应的 Dal 实例化,并保证 DbSession 在每个进程内都是唯一的
DbSession :整个数据库和数据访问层的会话
1.所有 Dal 的实例
2.SaveChanges ( ) 方法
单元工作模式:权利交给客户端,减少交互次数
应用:批量处理
通过 Spring.Net 注入
namespace ZSZ.DALFactory { public partial class DbSession : IDbSession { #region 通过 Spring.Net 注入
public ISettingDal SettingDal { get; set; } #endregion
public int SaveChanges() { return DbContextFactory.GetCurrentDbContext().SaveChanges(); } } }
通过 AutoFac 注入
public partial class DbSession : IDbSession { #region 通过 AutoFac 注入
public IAdminLogDal AdminLogDal { get { return DependencyResolver.Current.GetService<IAdminLogDal>(); } } #endregion
public int SaveChanges() { return DbContextFactory.GetCurrentDbContext().SaveChanges(); } }
DbSessionFactory
using DotNetMVC.IDAL; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Remoting.Messaging; using System.Text; using System.Threading.Tasks; namespace DotNetMVC.DALFactory { public class DbSessionFactory { public static IDbSession GetCurrentDbSession() { //return new DbSession();
IDbSession dbSession = CallContext.GetData("DbSession") as IDbSession; if (dbSession == null) { dbSession = new DbSession(); CallContext.SetData("DbSession", dbSession); } return dbSession; } } }
七、Service 层
基类:BaseService
BaseService<T> 中的实例通过 Spring.Net 注入
public abstract class BaseService<T> where T : BaseEntity { public IBaseDal<T> CurrentDal { get; set; } public IDbSession DbSession { get { return DbSessionFactory.GetCurrentDbSession(); } } public BaseService() { //目的:CurrentDal 使用之前,初始化赋值
SetCurrentDal(); } /// <summary>
/// 抽象方法,要求子类必须实现 /// 目的:逼迫子类 CurrentDal 赋值 /// </summary>
public abstract void SetCurrentDal(); /*********公共代码*********/ }
public partial class AdminLogService:BaseService<AdminLog>, IAdminLogService { public override void SetCurrentDal() { CurrentDal = DbSession.AdminLogDal; } }
普通类:
public partial class UserService:BaseService<User>, IUserService { }
八、IService 层
层与层访问使用接口隔离
九、T4 模板
各层基础代码通过 T4 模板生成
十、DTO 层
DTO 一般是“扁平类”,也就是没有关联属性,都是普通类型属性。DTO有哪些属性取决于其他层要什么数据。DTO类似于三层架构中的 Model。