NHibernate的UnitOfWork+Repository模式实现

最近也是一直学习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.设计架构时一定要搞清楚每层的职责,面向对象的精粹是封装,把能隐藏的尽量隐藏起来.不然耦合度就是这样累加起来的.

写到这里了,请参考

 

源码

 

转载于:https://www.cnblogs.com/saisky/archive/2011/09/10/2173012.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值