从EF三层 到 DDD领域驱动设计(1)--------------数据操作
前言:
一路走来,从单片机到C#,从电子行业到计算机行业,技术是不断提高,但是身体确不断变差,本来高新的工作也不得不辞掉,改到一家国企过着咸鱼般,但很爽的生活。
最近的金砖比较忙,刚忙完金砖的事,就有点时间写点东西。
DDD领域驱动设计,对于我这个做了不到一年的C#程序员来说,第一次听说就感觉高大上,不过在这边工作又是实习生的身份,比较有时间,就写写文章抒发心情。
本文将主要对比三层结构与DDD结构分析他们之间的不同点与优劣。
EF三层结构:
上图是三层架构的结构图:
首先是对于数据的获取,1,通过EF从数据库中生成实体数据
新建BaseDal类:
BaseDal的作用是抽离出传统类对于数据库的基本增删改查的分页操作,
通过泛型对数据的操作
namespace DAl { public class BaseDal<T>:IBaseDal<T> where T:class { DbContext db = ContextFactory.GetContext(); DbSet<T> _dbset; public BaseDal() { _dbset = db.Set<T>(); } public T SelectById(int id) { return db.Set<T>().Find(id); } public bool DelByIds(int[] id) { int a = 0; for (int i = 0; i < id.Length; i++) { bool boo= DelById(id[i]); if (boo) a++; } if (a != id.Length) return false ; return true; } public bool Edit(T model) { db.Entry(model).State = EntityState.Modified; return db.SaveChanges() > 0; } public bool DelById(int id) { T u1 = db.Set<T>().Find(id); db.Set<T>().Remove(u1); return db.SaveChanges() > 0; } public bool Add(T model) { db.Set<T>().Add(model); return db.SaveChanges() > 0; } public List<T> Query(Expression<Func<T, bool>> where) { return _dbset.Where(where).ToList(); } public IQueryable<T> GetList(Expression<Func<T, bool>> whereLambda) { return db.Set<T>().Where(whereLambda); //IEnumerable:select * from userinfo //IQueryable:select * from userinfo where ... } public IQueryable<T> GetPageList<TKey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, TKey>> orderLambda, int pageSize, int pageIndex, out int counter) { counter = db.Set<T>().Where(whereLambda).Count(); return db.Set<T>().Where(whereLambda) .OrderByDescending(orderLambda) .Skip((pageIndex - 1) * pageSize) .Take(pageSize); } } }
新建IBaseDal:BaseDal的接口类:
public partial interface IBaseDal<T> { bool DelByIds(int[] id); bool DelById(int id); T SelectById(int id); bool Add(T t); List<T> Query(Expression<Func<T, bool>> where); bool Edit(T model); IQueryable<T> GetList(Expression<Func<T, bool>> whereLambda); IQueryable<T> GetPageList<TKey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, TKey>> orderLambda, int pageSize, int pageIndex, out int counter); }
新建用户信息实体类UserInfo:继承BaseDal与IUserInfoDal接口
public partial class UserInfoDal:BaseDal<UserInfo1>,IUserInfoDal { }
IUserInfo:
public partial interface IUserInfoDal:IBaseDal<UserInfo1> { }
新建工厂类,通过
namespace FactoryDal { public partial class FactoryDal<T> where T:class { //简单工厂 public static IBaseDal<T> GetUserInfoDal() { return new BaseDal<T>(); } //抽象工厂 public static IBaseDal<T> CreateBaseDalInstance() { string assemblyName = System.Configuration.ConfigurationManager.AppSettings["Dal"]; Type ttype = typeof(T); //获取程序对象 Assembly al = Assembly.Load(assemblyName); //2.0.3 获取泛型类BaseDal<T>的封闭类型的type Type basedalType = al.GetType(assemblyName + ".BaseDal"); //3.0 通过反射的方式创建BaseDal`1类的对象实例 object obj = Activator.CreateInstance(basedalType); return obj as IBaseDal<T>; } }
分析系统的结构图如下:
在EF结构中DbContext是最主要的一个类,
网上是这么写的,表示看不大明白。
DbContext 是负责数据与对象互操作的主要的类型。它主要负责以下一些动作:
EntitySet : DbContext 包含所有从数据库表中被映射出来的实体对象的集合(如DbSet<TEntity>)。
Querying : DbContext 将LINQ To Entities 转化为SQL 查询语句并发送至数据库。
Change Tracking : 它保持变更追踪,一旦实体对象发生改变它就会从数据库中进行查询。
Persisting Data : 它也可以基于实体状态对数据库进行插入,更新和删除操作。
Caching : DbContext 默认作一级缓存,它存储在上下文类的生命周期中检索过的实体对象。
Manage Relationship : 在DB-First 或 Model-First 中 DbContext 使用CSDL, MSL 和 SSDL 管理关系,在Code-First中使用流式API管理关系。
Object Materialization : DbContext 将原始的表数据转化至实体对象中。
可以
我是这么理解的:
DBcontext中文翻译为上下文,就是连接数据库的对象。
1,其中构造函数传入配置文件中的连接字符串名时,可以自动读取里面的值连接上数据库。
如下:配置文件
<add name="Model3Container" connectionString="metadata=res://*/Model3.csdl|res://*/Model3.ssdl|res://*/Model3.msl;provider=System.Data.SqlClient;provider connection string="data source=RTOCUMCGLGHMO32;initial catalog=EF三层;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
public partial class Model3Container : DbContext { public Model3Container() : base("name=Model3Container") { }
2,对于数据库增删该查的操作
db.Set<T>().Find(id);//查 db.Set<T>().Add(model);//增 db.Set<T>().Remove(u1);//删 db.Entry(model).State = EntityState.Modified; db.SaveChanges()//改
通过数据库生成的数据源继承自DbContext,
public partial class Model3Container : DbContext
通过工厂模式实例化 Model3Container 对象,对数据库的操作。
程序在此下载:
http://download.csdn.net/download/quankangquan/9939162
参考文章 :http://www.cnblogs.com/landeanfen/p/4816706.html
接着是DDD的数据层准确的说DDD没有数据层,这里只是对于数据操作的提取。
DDD的概念:
参考文章中是这样描述的:
1、领域模型:领域模型与数据模型不同,它表述的是领域中各个类及其之间的关系。从领域驱动设计的角度看,数据库只不过是存储实体的一个外部机制,是属于技术层面的东西。数据模型主要用于描述领域模型对象的持久化方式,应该是先有领域模型,才有数据模型,领域模型需要通过某种映射而产生相应的数据模型,从这点来说,最新的EF的Code First就是一个很好的体现。领域模型对象分为实体、值对象和服务。
2、实体:在领域驱动设计里面,实体是模型中需要区分个体的对象。这里的实体和EntityFramework里面的实体不是一个概念,EF的实体为数据实体,不包含对象的行为。就博主的理解,DDD概念里面的实体就是包括实体数据(EF的Model)和行为的结合体。
3、值对象:通过对象属性值来识别的对象,它将多个相关属性组合为一个概念整体。相比实体而言,值对象仅仅是比实体少了一个标识。值对象的设计比较存在争议,我们暂且记住值对象和实体的区别:(1)实体拥有唯一标识,而值对象没有;(2)实体允许变化,而值对象不允许变化;(3)判断两个实体相等的方法是判断实体的标识相等,而判断两个值对象相等的标准是值对象内部所有属性值相等;
4、聚合(以及聚合根):聚合表示一组领域对象(包括实体和值对象),用来表述一个完整的领域概念。而每个聚合都有一个根实体,这个根实体又叫做聚合根。举个简单的例子,一个电脑包含硬盘、CPU、内存条等,这一个组合就是一个聚合,而电脑就是这个组合的聚合根。博主觉得关于聚合的划分学问还是挺大的,需要在实践中慢慢积累。同一个实体,在不同的聚合中,它可能是聚合根,也可能不是,需要根据实际的业务决定。聚合根是聚合所表述的领域概念的主体,外部对象需要访问聚合内的实体时,只能通过聚合根进行访问,而不能直接访问。
5、领域服务:博主的理解,领域模型主张富领域模式,也就是说把领域逻辑尽量写在领域实体里面,也就是常说的“充血模式”,而对于业务逻辑,最好是以服务的形式提供。至于领域逻辑和业务逻辑的界定,这个要根据实际情况来定。总之,领域服务是用来处理那些领域模型里面不好定义或者某些可变逻辑的的时候才会用到。待验证!
6、工厂、仓储等概念留在Demo里面说明。
我认为DDD与传统三层不同在于
1,设计的出发点不同,三层式基于数据的操作而设计的模式
DDD是基于领域(也就是功能的模块)的不同而设计的。
2,设计的使用场所不一样:三层主要用于中小型,功能改动不是很大,技术比较少或者不更新的项目中
DDD是为后期功能修改比较多,技术不断更新的项目中设计的。
下面是对于领域驱动设计的描述:
下面我们对参考文章中的项目进行分析:项目下载:https://pan.baidu.com/s/1nt6GuCT :来自:懒得安分 的博文
看到这个框架有点晕,找个突破点:1,页面的登陆,
2,EF数据源的。
这边从EF数据源分析起:找到领域层的.edmx 数据源在领域层中
我们知道想要与EF建立关系必须要创建EF实例对象 也就是实例化ESTMContainer
通过查找我们找到了部分类 的MEF导出 [Export(typeof(DbContext))]
[Export(typeof(DbContext))] public partial class ESTMContainer:DbContext { }
通过查找typeof(DbContext)
我们在基础层找到了实例化对象
public class EFUnitOfWork : IEFUnitOfWork { #region 属性 //通过工作单元向外暴露的EF上下文对象 public DbContext context { get { return EFContext; } } [Import(typeof(DbContext))] public DbContext EFContext { get; set; }
接着我们来单纯提取数据层测试:
http://download.csdn.net/download/quankangquan/9946591