如何抽象NHibernate以避免紧密依赖并方便测试

如何抽象NHibernate以避免紧密依赖并方便测试,nhibernate,testing,abstraction,Nhibernate,Testing,Abstraction,是否可以采用类似NHibernate或Entity框架的O/RM,并以这样的方式对其进行抽象,以便在遇到O/RM无法处理的情况时可以替换它 创建一个具有大块服务方法的服务似乎很有诱惑力,其中创建了一个会话,该会话用于获取/上传实体,然后用于保存所有脏对象 我会考虑存储库模式,以便服务操作向存储库请求实体,并且O/RM会话嵌入到存储库中。但是如何保存相关的实体,更新(T实体)方法是否立即刷新更改。这似乎很简单,但总体上并不令人满意 我现在倾向于使用一个O/RM包装器类,它公开了一个带有通用方法的接

是否可以采用类似NHibernate或Entity框架的O/RM,并以这样的方式对其进行抽象,以便在遇到O/RM无法处理的情况时可以替换它

创建一个具有大块服务方法的服务似乎很有诱惑力,其中创建了一个会话,该会话用于获取/上传实体,然后用于保存所有脏对象

我会考虑存储库模式,以便服务操作向存储库请求实体,并且O/RM会话嵌入到存储库中。但是如何保存相关的实体,更新(T实体)方法是否立即刷新更改。这似乎很简单,但总体上并不令人满意

我现在倾向于使用一个O/RM包装器类,它公开了一个带有通用方法的接口,如“StartSession”、“EndSession”、“放弃Session”、“GetById(对象id)”等

至少这将允许OR/M在测试中被伪造,这是我的另一大担忧

我想我是说我不想把业务逻辑和O/RM数据访问代码紧密地交织在一起,因为切换到另一个O/RM可能会导致大部分代码被替换


人们在现实世界中做什么?

我使用工作单元模式处理数据库中的C/U/D事务。我不想在抽象方面走得更远,因为我不想抽象这个抽象的抽象。Linq是查询部分的良好抽象,通用存储库是查询部分的良好抽象。这些是我在几乎所有具有关系数据库的项目中使用的抽象:

interface IRepository<T> : IQueryable<T>
{
   void Add(T entity);
   void Remove(T entity);
   T Get(Guid id);
}

interface IUnitOfWork : IDisposable
{
   void RollBack();
   void Commit();
}

// I don't do this every time, the generic repository is enough for almost everything.
[Example]
interface IOrderRepository : IRepository<Order>
{
   IList<Order> GetOrdersForUser(User user);
}

// This interface is only used in the repository implementation
interface INHiberanteUnitOfWork : IUnitOfWork
{
  ISession Session { get; }
}
接口IRepository:IQueryable
{
无效添加(T实体);
无效删除(T实体);
T Get(Guid id);
}
接口IUnitOfWork:IDisposable
{
无效回滚();
无效提交();
}
//我并不是每次都这样做,通用存储库几乎可以处理所有事情。
[示例]
接口存储库:IRepository
{
IList GetOrdersForUser(用户);
}
//此接口仅在存储库实现中使用
iberAnteUnitOfWork中的接口:IUnitOfWork
{
ISession会话{get;}
}
有时,我使用通用存储库,有时我使用具有规范模式支持的更具体的通用存储库,有时我为特定实体类型创建单独的存储库。 您可以在NHibernate+工作单元上搜索堆栈溢出,以查找有关实现这些模式的更多信息


我不抽象映射,因为每个orm在映射中都有自己的特性,我认为映射已经是一种抽象。我使用fluentnhibernate进行映射。

一个选项是将域模型定义为POCO,然后使用在自定义类型和基础OR/M之间进行代理。这允许您在需要时交换基础OR/M,另外,还要为不依赖数据库的单元测试模拟一个iQueryable源:-)

我的强烈意见是在较小的项目上进行一些测试,并选择一个工具集提前使用,而不是通过构建一个抽象包装器来推迟到以后再做决定,您将能够“有一天”进行更改。根据我的经验,总有一天不会到来,你所带来的复杂性是不值得的

在过去,我曾尝试为nHibernate构建一个通用包装器,但由于它,除了痛苦之外,我什么也没有遇到。ORM工具本身就是一个包装器和工具集。当您尝试创建一个包装器的包装器时,您最终需要开发(并随后维护)相对复杂的工具和机制来完成您正在包装的东西本机完成的工作

根据我自己的经验,我花了大量时间构建的包装器最终只暴露了底层ORM工具的一小部分功能。然后我再也没有用过包装纸,除了nHibernate。。。因此,我可以节省自己大量的时间和精力,只使用香草nHibernate本身

我建议您仔细研究一下您的需求,不要花时间抽象出一个瑞士军刀解决方案,而是先做研究,选择一个工具集并使用它运行。是的,您的代码将与第三方工具紧密耦合,但是代码将更简单,您将更快地到达目的地,并且结果可能会更好

希望有帮助


Max Schilling

您能否进一步解释“保存相关实体,并使用更新(T实体)方法即时刷新更改”。你为什么/怎么会觉得不满意呢。NHibernate唯一的非poco方面是:1。当您想使用延迟加载时,使用虚拟方法。2.(公共或非公共)默认构造函数。1.如果您想使用延迟加载,那么在运行时任何工具都不会给您带来任何好处,因为仍然需要虚拟机来代理POCO进行延迟加载。这只能通过代码生成工具或编译后过程来避免。2.我不明白为什么为了避免编写几个(愚蠢的)默认构造函数而使用另一个工具是值得的。BoostMap如何使您比NHiberante更具优势?不,不,您仍然会使用nhibernate(或EF,或Linq2Sql),但BoostMap允许您在or/M模型前面放置一个代理,这样您的代码就不必显式引用底层ORM特性来表达查询。我的意思是:BoostMap将带来什么好处,它不会删除对orm的依赖,它只是添加了另一个依赖。当然,它不会删除对orm的依赖,如果它删除了,它只会是另一个orm。在这种情况下,它允许您的代码删除对ORM代码的任何显式引用(至少对于查询而言)。。。正如最初的海报所说,如果需要的话,你可以把它换成另一个。对于创建/更新/删除场景,另一个抽象可以很容易地工作。。。例如,即使是r