使用NHibernate 3.2实现Repository(ORuM)(二)DDD、Entity、ValueObject、Repository、ORM、NHibernate
领域驱动设计(简称 DDD) 的提出是由Eric Evans在其《Domain-Driven Design –Tackling Complexity in the Heart of Software》(中文译名:领域驱动设计—软件核心复杂性应对之道)一书提出。
领域驱动设计事实上是针对OOAD的一个扩展和延伸,DDD基于面向对象分析与设计技术,对技术架构进行了分层规划,同时对每个类进行了策略和类型的划分。
领域模型是领域驱动的核心。采用DDD的设计思想,业务逻辑不再集中在几个大型的类上,而是由大量相对小的领域对象(类)组成,这些类具备自己的状态和行为,每个类是相对完整的独立体,并与现实领域的业务对象映射。领域模型就是由这样许多的细粒度的类组成。基于领域驱动的设计,保证了系统的可维护性、扩展性和复用性,在处理复杂业务逻辑方面有着先天的优势。
领域驱动设计的分层架构分为四层,其核心就是领域层(Domain),所有的业务逻辑应该在领域层实现,具体描述如下:
用户界面/展现层:负责向用户展现信息以及解释用户命令。
应用层:很薄的一层,用来协调应用的活动。它不包含业务逻辑。它不保留业务对象的状态,但它保有应用任务的进度状态。
领域层:本层包含关于领域的信息。这是业务软件的核心所在。在这里保留业务对象的状态,对业务对象和它们状态的持久化被委托给了基础设施层。
基础设施层:本层作为其他层的支撑库存在。它提供了层间的通信,实现对业务对象的持久化,包含对用户界面层的支撑库等作用。
领域驱动设计除了对系统架构进行了分层描述,还对对象(Object)做了明确的职责和策略划分:
实体(Entity):具备唯一ID,能够被持久化,具备业务逻辑,对应现实世界业务对象。
值对象(Value object):不具有唯一ID,由对象的属性描述,一般为内存中的临时对象,可以用来传递参数或对实体进行补充描述。
工厂(Factory):主要用来创建实体,目前架构实践中一般采用IOC容器来实现工厂的功能。
仓储(Repository):用来管理实体的集合,封装持久化框架。
服务(Service):为上层建筑提供可操作的接口,负责对领域对象进行调度和封装,同时可以对外提供各种形式的服务。
实体基类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using MVCQuick.Models;
namespace MVCQuick.Framework
{
public abstract class EntityBase
{
///<summary>
///
///</summary>
public virtual int Id { get; set; }
///<summary>
///
///</summary>
public virtual int Version{ get; set; }
///<summary>
///
///</summary>
///<param name="other"></param>
///<returns></returns>
public override bool Equals(object other)
{
if (this == other) return true;
EntityBase entity = other as EntityBase;
if (entity == null) return false;
if (!this.GetType().Equals(other.GetType()))return false;
if (!Id.Equals(entity.Id)) return false;
return true;
}
///<summary>
///
///</summary>
///<returns></returns>
public override int GetHashCode()
{
unchecked
{
int result;
result = this.GetType().GetHashCode();
result = 29 * result + Id.GetHashCode();
return result;
}
}
}
}
值对象接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MVCQuick.Framework
{
public interface IValueObject
{
}
}
实体类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MVCQuick.Framework;
namespace MVCQuick.Models
{
public class Genre : EntityBase
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual IEnumerable<Album> Albums { get; set; }
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MVCQuick.Framework;
namespace MVCQuick.Models
{
public class Artist : EntityBase
{
public virtual string Name { get; set; }
public virtual Address Address { get; set; }
public virtual IEnumerable<Album> Albums { get; set; }
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MVCQuick.Framework;
namespace MVCQuick.Models
{
public class Album : EntityBase
{
public virtual string Title { get; set; }
public virtual decimal Price { get; set; }
public virtual string AlbumArtUrl { get; set; }
public virtual Genre Genre { get; set; }
public virtual Artist Artist { get; set; }
}
}
值对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MVCQuick.Framework;
namespace MVCQuick.Models
{
public class Address : IValueObject
{
public virtual string City { get; set; }
public virtual string Country { get; set; }
public virtual string State { get; set; }
public virtual string Street { get; set; }
public virtual string Zip { get; set; }
}
}
为每种需要全局访问的对象类型创建一个对象,该对象为该类型所有对象在内存中的集合提供影像。用一个众所周知的全局访问接口来设立访问入口,提供增删对象的方法,把数据存储的实际的插入和删除封装起来。提供根据某种标准率选对象的方法,返回完整实例化了的属性值符合标准对象或对象的集合,把实际的存储和查询技术封装起来。仅为确实需要直接访问的聚合根提供仓储。让客户聚焦于模型,把所有对象存储和访问的工作委托给仓储来完成。
仓储具有许多优点:
它们为客户提供了一个简单的模型,来获取持久对象并管理其生命周期
它们把应用和领域设计从持久技术、多种数据库策略或甚至多种数据库来源解耦出来
它们传达了对象访问的设计决策
它们可以很容易被替换为哑实现,以便在测试中使用(通常使用一个内存中的集合)
仓储接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MVCQuick.Framework.Repository
{
public interface ITransaction : IDisposable
{
void Commit();
void Rollback();
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MVCQuick.Framework.Repository
{
public interface IRepository<TKey, TEntity> : IDisposable
{
void Save(TEntity entity);
void Update(TEntity entity);
void SaveOrUpdate(TEntity entity);
void Delete(TEntity entity);
TEntity Get(TKey id);
IList<TEntity> FindAll();
IList<TEntity> Find(string propertyName, object value);
ITransaction BeginTransaction();
}
}
ORM(Object/Relation Mapping,对象-关系映射),是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。
NHibernate 是一个基于.Net 的ORM工具。NHibernate 来源于非常优秀的基于Java的ORM工具——Hibernate。
NHibernate不仅仅管理.NET类到数据库表的映射(包括.NET 数据类型到SQL数据类型的映射),还提供数据查询和获取数据的方法,可以大幅度减少开发时人工使用SQL和ADO.NET处理数据的时间。
NHibernate的目标是对于开发者通常的数据持久化相关的编程任务,解放其中的95%。对于以数据为中心的程序来说,它们往往只在数据库中使用存储过程来实现商业逻辑,NHibernate可能不是最好的解决方案;对于那些在基于.NET的中间层应用中,它们实现面向对象的业务模型和商业逻辑的应用,NHibernate是最有用的。不管怎样,NHibernate一定可以帮助你消除或者包装那些针对特定厂商的SQL代码,并且帮你把结果集从表格式的表示形式转换到一系列的对象中去。
NHibernate实现仓储
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate;
using NHibernate.Criterion;
using System.Data;
namespace MVCQuick.Framework.Repository.NHibernate
{
public class NHibernateRepository<TKey, TEntity> : IRepository<TKey, TEntity> where TEntity : class
{
private ISessionFactory sessionFactory;
private ISession session;
public NHibernateRepository(ISessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
this.session = sessionFactory.OpenSession();
}
public NHibernateRepository(ISession session)
{
this.session = session;
}
public ISessionFactory SessionFactory
{
get { return this.sessionFactory; }
}
public ISession Session
{
get { return this.session; }
}
public void Save(TEntity entity)
{
ISession session = Session;
session.Save(entity);
session.Flush();
}
public void Update(TEntity entity)
{
ISession session = Session;
session.Update(entity);
session.Flush();
}
public void SaveOrUpdate(TEntity entity)
{
ISession session = Session;
session.SaveOrUpdate(entity);
session.Flush();
}
public void Delete(TEntity entity)
{
ISession session = Session;
session.Delete(entity);
session.Flush();
}
public TEntity Get(TKey id)
{
ISession session = Session;
return session.Get<TEntity>(id);
}
public IList<TEntity> FindAll()
{
ISession session = Session;
ICriteria criteria = session.CreateCriteria(typeof(TEntity));
return criteria.List<TEntity>();
}
public IList<TEntity> Find(string propertyName, object value)
{
if (value == null)
return Find(Expression.IsNull(propertyName));
if (value is string)
return Find(Expression.Like(propertyName, value));
return Find(Expression.Eq(propertyName, value));
}
public IList<TEntity> Find(params ICriterion[] criteria)
{
ISession session = Session;
ICriteria crit = RepositoryHelper<TEntity>.CreateCriteriaFromArray(session, criteria);
return crit.List<TEntity>();
}
public IList<TEntity> Find(Order[] orders, params ICriterion[] criteria)
{
ISession session = Session;
ICriteria crit = RepositoryHelper<TEntity>.CreateCriteriaFromArray(session, criteria);
foreach (Order order in orders)
{
crit.AddOrder(order);
}
return crit.List<TEntity>();
}
public IList<TEntity> Find(
int firstResult, int numberOfResults, Order[] selectionOrder, params ICriterion[] criteria)
{
ISession session = Session;
ICriteria crit = RepositoryHelper<TEntity>.CreateCriteriaFromArray(session, criteria);
crit.SetFirstResult(firstResult)
.SetMaxResults(numberOfResults);
foreach (Order order in selectionOrder)
{
crit.AddOrder(order);
}
return crit.List<TEntity>();
}
public ITransaction BeginTransaction()
{
ISession session = Session;
return new NHibernateTransaction(session);
}
private void CloseSession()
{
session.Close();
session.Dispose();
session = null;
}
public void Dispose()
{
if (this.session != null)
{
this.session.Flush();
CloseSession();
}
}
}
internal class RepositoryHelper<T>
{
public static ICriteria CreateCriteriaFromArray(ISession session, ICriterion[] criteria)
{
ICriteria crit = session.CreateCriteria(typeof(T));
foreach (ICriterion criterion in criteria)
{
if (criterion == null)
continue;
crit.Add(criterion);
}
return crit;
}
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NH=NHibernate;
namespace MVCQuick.Framework.Repository.NHibernate
{
public class NHibernateTransaction : ITransaction
{
NH.ITransaction transaction;
bool isOriginator = true;
public NHibernateTransaction(NH.ISession session)
{
transaction = session.Transaction;
if (transaction.IsActive)
isOriginator = false; // The method that first opened the transaction should also close it
else
transaction.Begin();
}
#region ITransaction Members
public void Commit()
{
if(isOriginator && !transaction.WasCommitted && !transaction.WasRolledBack)
transaction.Commit();
}
public void Rollback()
{
if (!transaction.WasCommitted && !transaction.WasRolledBack)
transaction.Rollback();
}
#endregion
#region IDisposable Members
void IDisposable.Dispose()
{
if(isOriginator)
{
Rollback();
transaction.Dispose();
}
}
#endregion
}
}