zoukankan      html  css  js  c++  java
  • NHibernate实战详解(一)领域模型设计

    关于NHibernate的资料本身就不多,中文的就更少了,好在有一些翻译文章含金量很高,另外NHibernate与Hibernate的使用方式可谓神似,所以也有不少经验可以去参考Hibernate。

    本文是实战中的心得,也是NHibernate进阶教程,假设你已经看过NHibernate的文档,但对它还是觉得无法驾驭,那么你可以看看本文,或者你只是想看看其他人在实战中是如何使用它的,你也可以看看。

    本文主要会涉及到这些概念,关键字:级联操作 多表查询 复杂查询 值对象

    需求简述:

    简单地描述一下,有一个批次,一个批次包含多个订单,每个订单又可以有3个任务步骤要处理。

    有时候我们需要获取一个批次,然后对这个批次下所有订单进行处理,也可能会涉及到订单的任务(数据库中可能涉及到3张有关联的表);

    有时候我们也会获取一个订单,然后看这个订单属于哪个批次,还有就是对这个订单的任务步骤进行操作;

    有时候我们会有相对复杂的查询,比如说要显示到第二个任务步骤的订单,并且第一个任务步骤已经完成,然后还需要根据订单中的日期进行过滤;

    业务规则:

    一个批次可以有多个订单,订单号不能重复;

    一个订单有3个任务步骤,而且是三种不同类型的任务步骤;

    也就是说如果我们获取了一个批次对象,它可能包含了40个订单,每个订单最多有4种任务类型,但转换成数据库SQL查询语句可能会返回最多160条记录。

    关注领域模型,以领域模型为中心

    图1-1 领域模型图

    上图可以看出批次、订单、任务三个领域模型之间的关系。

    批次,批次被创建的时候(也就是new实例化的时候),它的创建时间(CreateDate)就是系统默认时间,

    然后他有一个批次名,并且它依赖了一个不重复的订单集合。

    你会发现这里使用的是Isei类库,是因为.NET 2.0没有Set集合,需要引用第三方类库。

    另外一些操作订单的方法里都会有一句“order.PurchaseTime = null;”或者"order.PurchaseTime = this;",

    这表示我们在批次中添加订单的同时,让订单对象也关联到批次,让订单对象可以感知到批次的存在,这一点非常重要,否则NHibernate无法执行级联操作。

    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会从内存中去比较他们是不是同一个对象的引用,而在这里基本上不会是同一个引用,如果它们的某些属性相同,我们就认为它们是已经存在,可以用重写Equals和GetHashCode方法来做到这一点。

    但是我们上面的需求提到验证某种任务类型存不存在,所以我们主要关心的不是ID相不相等,而是类型(tasktype)相不相等,后面会提到如何重写任务模型中的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重写比较对象相等的Equals和GetHashCode方法了,当我们调用Contains方法来判断任务集合中是否已经包含这个任务时,将会自动调用Equals、GetHashCode方法。

    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();
            }           
    }

    总结: 

            我们介绍了领域模型的设计,你会发现本文提到的模型它们之间都是一对多关系,模型包含了许多业务,看起来非常复杂,其实,这个复杂的模型并不是一开始就建成的,开始时它非常简单,也许最简单的时候只有一个UML类图的草稿只包含了对象名称,然后逐步细化。下一节中将会看到映射文件(hbm.xml)的编写,与一些级联的操作(级联保存、多表查询)的应用。

  • 相关阅读:
    【Flask-RESTPlus系列】Part3:请求解析
    【Flask-RESTPlus系列】Part2:响应编组
    【Flask-RESTPlus系列】Part1:快速入门
    webpack中hash、chunkhash、contenthash区别
    如何将现有 git 仓库中的子项目单独抽出来作为一个独立仓库并保留其提交历史
    Using Immutable in React + React-Redux
    ChromeExtension那些事儿
    Get started with Google Analytics
    middlewares in GCC
    实现一个简单的虚拟DOM
  • 原文地址:https://www.cnblogs.com/13yan/p/3494921.html
Copyright © 2011-2022 走看看