Dan*_*rth 16 .net c# nhibernate orm unit-testing
在我的项目中,我使用以下方法从数据库中查询数据:
IRepository.Get<T>代替IRepository<T>.Get.NHibernates ISession就是这种存储库的一个例子.使用IQueryable<T>特定的扩展方法T来封装重复查询,例如
public static IQueryable<Invoice> ByInvoiceType(this IQueryable<Invoice> q,
InvoiceType invoiceType)
{
return q.Where(x => x.InvoiceType == invoiceType);
}
Run Code Online (Sandbox Code Playgroud)用法如下:
var result = session.Query<Invoice>().ByInvoiceType(InvoiceType.NormalInvoice);
Run Code Online (Sandbox Code Playgroud)
现在假设我有一个我想要测试的公共方法使用此查询.我想测试三种可能的情况:
我现在的问题是:要嘲笑什么?
ByInvoiceType因为它是一种扩展方法,或者我可以吗?Query同样的理由.Dan*_*rth 30
经过一些研究并根据这里的答案和这些 链接,我决定完全重新设计我的API.
基本概念是完全禁止业务代码中的自定义查询.这解决了两个问题:
IQueryable<T>,哪些不是.在业务代码中,查询现在看起来像这样:
IEnumerable<Invoice> inv = repository.Query
.Invoices.ThatAre
.Started()
.Unfinished()
.And.WithoutError();
// or
IEnumerable<Invoice> inv = repository.Query.Invoices.ThatAre.Started();
// or
Invoice inv = repository.Query.Invoices.ByInvoiceNumber(invoiceNumber);
Run Code Online (Sandbox Code Playgroud)
在实践中,这是这样实现的:
正如Vytautas Mackonis在他的回答中所说,我不再直接依赖于NHibernate ISession,而是我现在依赖于IRepository.
此接口具有名为Querytype 的属性IQueries.对于业务层需要查询的每个实体,都有一个属性IQueries.每个属性都有自己的接口,用于定义实体的查询.每个查询接口都实现了通用IQuery<T>接口,而接口又实现了IEnumerable<T>,从而产生了如上所述的非常干净的DSL语法.
一些代码:
public interface IRepository
{
IQueries Queries { get; }
}
public interface IQueries
{
IInvoiceQuery Invoices { get; }
IUserQuery Users { get; }
}
public interface IQuery<T> : IEnumerable<T>
{
T Single();
T SingleOrDefault();
T First();
T FirstOrDefault();
}
public interface IInvoiceQuery : IQuery<Invoice>
{
IInvoiceQuery Started();
IInvoiceQuery Unfinished();
IInvoiceQuery WithoutError();
Invoice ByInvoiceNumber(string invoiceNumber);
}
Run Code Online (Sandbox Code Playgroud)
这种流畅的查询语法允许业务层组合提供的查询,以充分利用底层ORM的功能,让数据库尽可能地过滤.
NHibernate的实现看起来像这样:
public class NHibernateInvoiceQuery : IInvoiceQuery
{
IQueryable<Invoice> _query;
public NHibernateInvoiceQuery(ISession session)
{
_query = session.Query<Invoice>();
}
public IInvoiceQuery Started()
{
_query = _query.Where(x => x.IsStarted);
return this;
}
public IInvoiceQuery WithoutError()
{
_query = _query.Where(x => !x.HasError);
return this;
}
public Invoice ByInvoiceNumber(string invoiceNumber)
{
return _query.SingleOrDefault(x => x.InvoiceNumber == invoiceNumber);
}
public IEnumerator<Invoice> GetEnumerator()
{
return _query.GetEnumerator();
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
在我的实际实现中,我将大部分基础结构代码提取到基类中,因此为新实体创建新的查询对象变得非常容易.向现有实体添加新查询也非常简单.
关于这一点的好处是业务层完全没有查询逻辑,因此可以轻松切换数据存储.或者可以使用条件API实现其中一个查询,或者从另一个数据源获取数据.业务层将忽略这些细节.
| 归档时间: |
|
| 查看次数: |
4249 次 |
| 最近记录: |