领域模型设计
2013-12-28 16:39
197 查看
NHibernate实战详解(一)领域模型设计
关于NHibernate的资料本身就不多,中文的就更少了,好在有一些翻译文章含金量很高,另外NHibernate与Hibernate的使用方式可谓神似,所以也有不少经验可以去参考Hibernate。本文是实战中的心得,也是NHibernate进阶教程,假设你已经看过NHibernate的文档,但对它还是觉得无法驾驭,那么你可以看看本文,或者你只是想看看其他人在实战中是如何使用它的,你也可以看看。
所以,本文提到的内容绝对是干货。
本文主要会涉及到这些概念,关键字:级联操作 多表查询 复杂查询 值对象
需求简述:
简单地描述一下,有一个批次,一个批次包含多个订单,每个订单又可以有3个任务步骤要处理。
有时候我们需要获取一个批次,然后对这个批次下所有订单进行处理,也可能会涉及到订单的任务(数据库中可能涉及到3张有关联的表);
有时候我们也会获取一个订单,然后看这个订单属于哪个批次,还有就是对这个订单的任务步骤进行操作;
有时候我们会有相对复杂的查询,比如说要显示到第二个任务步骤的订单,并且第一个任务步骤已经完成,然后还需要根据订单中的日期进行过滤;
业务规则:
一个批次可以有多个订单,订单号不能重复;
一个订单有3个任务步骤,而且是三种不同类型的任务步骤;
也就是说如果我们获取了一个批次对象,它可能包含了40个订单,每个订单最多有4种任务类型,但转换成数据库SQL查询语句可能会返回最多160条记录。
关注领域模型,以领域模型为中心
图1-1 领域模型图
上图可以看出三个领域模型之间都存在着密切的关系。
批次类,批次被创建的时候(也就是new实例化的时候),它的创建时间(CreateDate)就是系统默认时间,
然后他有一个名字(Name),并且它依赖了一个集合(订单集合),和一些方法,一些操作订单的方法。
你会发现我这里使用的是Isei类库,大家都知道这个是表示这个集合不能重复的。
另外一些操作订单的方法里都会有一句“order.PurchaseTime = null;”或者"order.PurchaseTime = this;",
这表示我们在批次中添加订单的同时,让订单对象也关联到批次,让订单对象可以感知到批次的存在,这一点非常重要。
public class PurchaseTime : DomainBase { public PurchaseTime() { CreateDate = DateTime.Now; Status = "0"; SalesmanStatus = "0"; StorageStatus = "0"; } /// </summary> public virtual string Name { get; set; } private ICollection<PurchaseOrder> _purchaseOrders = new Iesi.Collections.Generic.HashedSet<PurchaseOrder>(); public virtual ICollection<PurchaseOrder> PurchaseOrders { get { return _purchaseOrders; } set { _purchaseOrders = value; } } public virtual void ClearOrders() { foreach (PurchaseOrder order in this._purchaseOrders) { order.PurchaseTime = null; } this._purchaseOrders.Clear(); } public virtual void AddOrder(PurchaseOrder order) { order.PurchaseTime = this; this._purchaseOrders.Add(order); } public virtual void RemoveOrder(PurchaseOrder order) { order.PurchaseTime = null; this._purchaseOrders.Remove(order); } }
订单类,这个订单的业务逻辑已经被我砍掉很多了,只保留一些我们要讨论的内容,本来它的内容相当丰富。
可以看到顶到依赖一个批次对象,并且可以拥有任务集合。
AppointTask方法里面包含了许多逻辑,首先会调用ContainsTask判断该订单是否已经有这个任务步骤,它只是执行这样一句“_purchaseTasks.Contains(task);”,
意思是看一下该订单的任务集合中是不是有相同的任务对象存在,这里非常有意思,通常比较两个对象是否相等.NET会从内存中去比较他们是不是同一个对象,而使用NHibernate的话我们可能会希望他们的ID属性是不是同一个来判断他们是否相等,所以我们一般会涉及一个所有领域模型的基类,来重写Equals和GetHashCode方法。
但是我们上面的需求提到验证任务类型存不存在,所以我们关心的不是ID相不相等,而是类型(takstype)相不相等,后面会提到如何重写Equals和GetHashCode方法。
这个领域模型的设计已经给后面的设计打下了重要的基础,为什么我们不写存储过程,或者是再查询一次数据库来验证是否存在或者什么的。
public class PurchaseOrder : DomainBase { /// <summary> /// 订单号 /// </summary> public virtual string OrderNumber { get; set; } /// <summary> /// 交货日期 /// </summary> public virtual DateTime? DateOfDelivery { get; set; } private PurchaseTime _purchaseTime = new PurchaseTime(); public virtual PurchaseTime PurchaseTime { get { return _purchaseTime; } set { _purchaseTime = value; } } private ICollection<PurchaseTask> _purchaseTasks = new HashedSet<PurchaseTask>(); public virtual ICollection<PurchaseTask> PurchaseTasks { get { return _purchaseTasks; } set { _purchaseTasks=value; } } /// <summary> /// 指派任务 /// </summary> /// <param name="task"></param> public virtual void AppointTask(PurchaseTask task) { if (ContainsTask(task)) { if (TaskProgress == 0) { RemoveTask(task); } else { throw new Exception("任务已经开始,无法重新指派!"); } } if (task.Principal != 0) { task.PurchaseOrder = this; _purchaseTasks.Add(task); } } public virtual void ClearTask() { foreach (PurchaseTask item in _purchaseTasks) { item.PurchaseOrder = null; } _purchaseTasks.Clear(); } public virtual void RemoveTask(PurchaseTask task) { task.PurchaseOrder = null; _purchaseTasks.Remove(task); } public virtual bool ContainsTask(PurchaseTask task) { return _purchaseTasks.Contains(task); } /// <summary> /// 任务进度 /// 0未开始 /// 1完成 /// 2完成 /// 3完成 /// </summary> public virtual decimal? TaskProgress { get; set; } }
任务类,在这个类的最后我们已经看到override比较对象相等的方法了。
public class PurchaseTask : DomainBase { private PurchaseOrder _purchaseOrder = new PurchaseOrder(); /// <summary> /// PurchaseOrder /// </summary> public virtual PurchaseOrder PurchaseOrder { get { return _purchaseOrder; } set { _purchaseOrder=value; } } /// <summary> /// 任务类型 /// 类型1 类型2 类型3 /// </summary> public virtual decimal? TaskType { get; set; } /// <summary> /// 任务负责人ID /// </summary> public virtual decimal? Principal { get; set; } /// <summary> /// 是否处理 /// </summary> /// <returns></returns> public virtual bool IfHandle() { if (this.PurchaseOrder.TaskProgress ==GetNeedHandleProgress(this.TaskType)) { return true; } return false; } /// <summary> /// 获取需要处理的进度步骤 /// </summary> /// <param name="tasktype"></param> /// <returns></returns> public virtual decimal? GetNeedHandleProgress(decimal? tasktype) { switch (tasktype.ToString().ToLower()) { case "1": return 0; case "2": return 2; case "3": return 3; default: throw new Exception("任务类型错误!无法定位需要处理的进度。"); } } public override bool Equals(object obj) { PurchaseTask task = obj as PurchaseTask; if (task == null) return false; return TaskType.Equals(task.TaskType); } public override int GetHashCode() { return TaskType.GetHashCode(); } }
这一节就到这里,在下一节中将会看到映射文件(hbm.xml)的编写,与一些级联的操作(级联保存、多表查询)的应用。
相关文章推荐
- DDD领域驱动模型设计
- 从项目始末谈面向对象——领域分析、需求分析、分析模型、设计、实现
- 从领域、对象、角色、职责、对象交互、场景等方面去分析和设计领域模型(附源码)
- 领域模型设计讨论与研究
- 领域模型设计讨论与研究
- 贫血模型;DTO:数据传输对象(Data Transfer Object);AutoMapper ;Domain Model(领域模型);DDD(领域驱动设计)
- 【tornado】系列项目(一)之基于领域驱动模型架构设计的京东用户管理后台
- 系统设计的体系结构------领域(模型)和服务的划分
- TDD应用试例(根据领域驱动模型设计的培训内容)
- Dino Esposito: 一个领域模型的设计
- 领域驱动设计之领域模型
- 数据仓库专题20-案例篇:电商领域数据主题域模型设计v0.2(改进意见征集中)
- 数独设计-领域模型分析初步
- 认真的考虑了下领域模型,发现设计是最难的部分。书上的例子各个对象职责划分的不错,可惜能看懂不代表能设计出。
- 领域模型驱动设计读书笔记
- UML小结以及基于领域模型的系统设计初步
- Hibernate领域模型与数据库之间转化设计方法
- DDD 领域驱动设计-如何完善 Domain Model(领域模型)?
- 领域模型设计