最近也是一直学习NHibernate,前天看了Nic Pei的各种ORM框架一站式代码下载,阅读了其Demo源码,不爽之,主要原因有三:
1.架构和每层职责有点混乱,例如,在Service层有Repository的实现,UI层也有Repository和UnitOfWork.
2.UnitOfWork的实现方式.
3.IOC工具的引入.
不急于实现,先来看看项目的整体架构.
整体架构
盗用Mainz的一幅图
解决方案如下
我实现的整体架构类似这幅图,更喜欢把实现UnitOfWork+Repository的层叫Persistence(持久层)而不是Ifrastructure(基础设施).这种架构非常类似PetShop的三层架构,只不过是换个名字而已.(关于架构,参考这篇文章,谈谈对于企业级系统架构的理解),简单说几点.
1.我实现的架构的最重要指标是可测试性.(感觉只要实现可测试性,其它维护性,扩展性之类也实现了)
2上层依赖于下层的接口,下层具体实现对上层是不可见的(类的可见性为internal),是黑箱.见到了就耦合了.
3绝对不可以跨层引用,即Presentation(UI层,表现层)不应该引用Persistence(持久层)的任何类库.
Persistence(持久层)
层的职责和依赖
职责:持久化
依赖:Domain Model,配置文件里的数据库的配置方式,---(Ifrastructure(基础设施)就不提了,它横跨整个项目,每个类库都可以引用)
提供:以工厂(或服务定位器)的方式向外提供持久化接口的实现,提供方法用于生成数据库.
类图如下:
很普通的实现方式,一个接口+N个实现,在具体实现接口的程序集中,上层模块通过配置选择具体的工厂(或服务定位器)来获取实现.
IRepositoryBase
/// <summary> |
/// 基础仓储接口 |
/// </summary> |
public interface IRepositoryBase |
{ |
/// <summary> |
/// 获取实体 |
/// </summary> |
/// <typeparam name="TEntity">实体类型</typeparam> |
/// <param name="Id">Id</param> |
/// <returns>实体</returns> |
TEntity Get<TEntity>( long Id) where TEntity : IEntity; |
//其它方法省略 |
} |
注意这里我把泛型约束全部放在方法里,而不是类中,这样有助于方便下面的UnitOfWork中把它存起来,如果是泛型类就不好存.
RepositoryBase :
/// <summary> |
/// 基础仓储实现 |
/// </summary> |
internal abstract class RepositoryBase : IRepositoryBase |
{ |
private ISession m_Session; |
|
internal RepositoryBase(ISession session) |
{ |
m_Session = session; |
} |
public IList<TEntity> All<TEntity>() where TEntity : IEntity |
{ |
return this .m_Session.Query<TEntity>().ToList(); |
} |
//其它省略 |
} |
可见性为internal,依赖于NHibernate的ISession.
RepositoryFactory:
/// <summary>
/// 仓储工厂
/// </summary>
public static class RepositoryFactory
{
/// <summary>
/// 生成Person仓储
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
public static IPersonRepository CreatePersonRepository(IDisposable session)
{
if (session is ISession == false)
throw new InvalidOperationException("参数session不是有效的NHibernate的ISession");
return new PersonRepository((ISession)session);
}
/// <summary>
/// 生成工作单元
/// </summary>
/// <returns></returns>
public static IUnitOfWork CreateUnitOfWork()
{
return new UnitOfWork();
}
}
这里还应该有一个方法用于生成数据库,具体可参考我的源代码.再说几点.
Repository的实现类的可见性应该设为internal.但internal可见性会为单元测试带来不便,有两种解决方法.一是在在程序集内写单元测试,二是设置友元程序集(关于友元程序集),例如这里就是设置NHImplement.Test为NHImplement的友元程序集.
在AssemblyInfo.cs里:
1 | [assembly: InternalsVisibleTo( "NHUnitOfWork.Test.Persistence.Implement.NHibernate" )] |
实际项目中通常只有一种实现方式,也没有配置文件,直接依赖于工厂(或服务定位器).
依赖的数据库配置方式看实际确定,可以是一个数据库连接字符串,也可以是一个数据库的Provider,或复杂的组合,等等,
接口的类库不应该引用NHibernate或entity framework的dll,而应该在具体的实现类引用.
Service层
职责:持久化
依赖:Domain Model,Persistence(持久层)接口,Persistence(持久层)实现中的工厂(或服务定位器).
提供:以工厂(或服务定位器)的方式向外提供服务接口的实现.
类图如下:
如果持久层只有一种实现方式的话,那类图也跟这个一样.Service的实现类的可见性也应该设为internal,然后通过工厂(或服务定位器)获取服务的实现.
代码类似上面的IRepositoryBase,RepositoryBase和RepositoryFactory,但在我的小项目里就没有定义服务基类,因为我在Service层提供的是粗粒度的接口.
而且只有服务工厂才会调用Repository工厂,其它服务的实现类依赖的是Persistence(持久层)的接口.
PersonService
internal class PersonService : IPersonService
{
private IPersonRepository m_IPersonRepository;
private IUnitOfWork m_IUnitOfWork;
internal PersonService(IPersonRepository personRepository, IUnitOfWork unitOfWork)
{
this.m_IPersonRepository = personRepository;
this.m_IUnitOfWork = unitOfWork;
}
public void Add(Person person)
{
if (person == null)
throw new InvalidOperationException("person不能为空!");
person.Password = this.HashCode(person.Password);
this.m_IUnitOfWork.RegisterAdded(person, m_IPersonRepository);
this.m_IUnitOfWork.Commit();
}
}
1 | 服务工厂 |
/// <summary>
/// 服务工厂
/// </summary>
public static class ServiceFactory
{
/// <summary>
/// 获取Person服务
/// </summary>
public static IPersonService PersonService
{
get
{
var uow = RepositoryFactory.CreateUnitOfWork();
return new PersonService(RepositoryFactory.CreatePersonRepository(uow.SessionOrDBContext), uow);
}
}
}
UnitOfWork(工作单元)模式的实现
UnitOfWork(工作单元)模式就是把一系列的对数据库的操作(work)放到一个单元(unit)内,在NHibernate的Repository模式中实现UnitOfWork(工作单元)模式实际要做的就是所有Repository共享一个ISession就可以,unit就是这个ISession的一个事务,在什么时刻打开事务是个问题,比较好的是把Repository存起来,所有操作完成时再打开事务,再提交事务.
IUnitOfWork
/// <summary>
/// 工作单元模式接口
/// </summary>
public interface IUnitOfWork : IDisposable
{
void RegisterUpdate(IEntity entity, IRepositoryBase repository);
void RegisterAdded(IEntity entity, IRepositoryBase repository);
void RegisterDeleted(IEntity entity, IRepositoryBase repository);
/// <summary>
/// 提交事务
/// </summary>
/// <returns>key实体,value为Id</returns>
IDictionary<IEntity, long> Commit();
/// <summary>
/// 提交事务
/// </summary>
/// <param name="isolationLevel">事务级别</param>
/// <returns>key实体,value为Id</returns>
IDictionary<IEntity, long> Commit(IsolationLevel isolationLevel);
/// <summary>
/// 数据库上下文(NHibernate为ISession,EF叫DBContext)
/// </summary>
IDisposable SessionOrDBContext { get; set; }
}
NHibernate的UnitOfWork实现
internal class UnitOfWork : IUnitOfWork
{
private ISession m_Session;
protected Dictionary<IEntity, IRepositoryBase> m_AddedEntities;
protected Dictionary<IEntity, IRepositoryBase> m_UpdatedEntities;
protected Dictionary<IEntity, IRepositoryBase> m_DeletedEntities;
internal UnitOfWork()
{
this.m_Session = DatabaseManager.CurrentFactory.OpenSession();
this.m_AddedEntities = new Dictionary<IEntity, IRepositoryBase>();
this.m_UpdatedEntities = new Dictionary<IEntity, IRepositoryBase>();
this.m_DeletedEntities = new Dictionary<IEntity, IRepositoryBase>();
}
public void RegisterUpdate(IEntity entity, IRepositoryBase repository)
{
this.m_UpdatedEntities.Add(entity, repository);
}
public void RegisterAdded(IEntity entity, IRepositoryBase repository)
{
this.m_AddedEntities.Add(entity, repository);
}
public void RegisterDeleted(IEntity entity, IRepositoryBase repository)
{
this.m_DeletedEntities.Add(entity, repository);
}
public IDictionary<IEntity, long> Commit()
{
return Commit(IsolationLevel.Unspecified);
}
public IDictionary<IEntity, long> Commit(IsolationLevel isolationLevel)
{
var dic = new Dictionary<IEntity, long>();
using (var transaction = this.m_Session.BeginTransaction(isolationLevel))
{
this.m_AddedEntities.Keys.ToList().ForEach(entity =>
{
var id = this.m_AddedEntities[entity].Add(entity);
dic.Add(entity, id);
});
this.m_UpdatedEntities.Keys.ToList().ForEach(entity =>
{
this.m_UpdatedEntities[entity].Update(entity);
});
this.m_DeletedEntities.Keys.ToList().ForEach(entity =>
{
this.m_DeletedEntities[entity].Delete(entity);
});
transaction.Commit();
}
return dic;
}
public IDisposable SessionOrDBContext
{
get { return this.m_Session; }
set { }
}
public void Dispose()
{
if (this.m_Session.IsOpen)
this.m_Session.Close();
}
}
就这样用Dictionary把repository和要操作的实体存起来Commit时再打开事务,执行操作,提交事务,感觉这样事务的打开时间最短.
1.不要为使用Ioc而使用Ioc,没有它一样解耦,这里没有用Ioc,但感觉耦合比Nic Pei更低
2.在我自己的小项目中总怀疑有没有必要实现repository,把强大的ORM提供的方法都变成了几个接口?
3.设计架构时一定要搞清楚每层的职责,面向对象的精粹是封装,把能隐藏的尽量隐藏起来.不然耦合度就是这样累加起来的.
写到这里了,请参考