掌上租项目架构总结

        掌上租项目是如鹏 .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。

 

posted @ 2018-09-23 09:34  linyongqin  阅读(2377)  评论(0编辑  收藏  举报