我正在使用一个公开的存储库,它会像这样公开IQueryable<T>
:
public IQueryable<T> AllEntities
{
get
{
return session.Query<T>();
}
}
我可以这样查询:
var results =
(from e in repository.AllEntities
where e.SomeProperty == "some value"
select e).ToList();
但是,如果T
有父母和祖父母实体并且我想急切加载它们,我必须这样做:
var results =
(from e in repository.AllEntities
where e.SomeProperty == "some value"
select e)
.Fetch(x => x.Parent)
.ThenFetch(x => x.Grandparent)
.ToList();
这样可行,但.Fetch
和.ThenFetch
都是Linq2Nhibernate特定的扩展方法,这会导致两个问题:
我必须在我的文件顶部添加using NHibernate.Linq;
语句。但是,在我正在进行此查询时,它应该是与实现无关的。
当我尝试对此进行单元测试时,.Fetch
和.ThenFetch
方法在针对我的模拟存储库提供的IQueryable<T>
执行时失败。
如何将这些包装在我的IRepository<T>
界面内或某些通用扩展方法中?
更新
到目前为止,我所提出的只是将其添加到我的存储库界面:
IQueryable<T> EagerLoadParent<U>(IQueryable<T> query,
Expression<Func<T, U>> parentExpression);
IQueryable<T> EagerLoadParent<U, V>(IQueryable<T> query,
Expression<Func<T, U>> parentExpression,
Expression<Func<U, V>> grandparentExpression);
...这是我的NHibernate存储库实现:
public IQueryable<T> EagerLoadParent<U>(IQueryable<T> query,
Expression<Func<T, U>> parentExpression)
{
return query
.Fetch(parentExpression);
}
public IQueryable<T> EagerLoadParent<U, V>(IQueryable<T> query,
Expression<Func<T, U>> parentExpression,
Expression<Func<U, V>> grandparentExpression)
{
return query
.Fetch(parentExpression)
.ThenFetch(grandparentExpression);
}
此API的使用者现在执行此操作:
var query =
(from e in repository.AllEntities
where e.SomeProperty == "some value"
select e);
var results = repository
.EagerLoadParent(query, e => e.Parent, p => p.Grandparent)
.ToList();
但这缺乏我更喜欢的好的扩展方法语法。我正在寻找更接近.Fetch
和.ThenFetch
语法的内容。
答案 0 :(得分:13)
经过一些调查后,我认为我有一个方法:只需密切关注NHibernate.Linq
实现,以便拥有自己的实现并避免在客户端代码中明确的NHibernate.Linq依赖。你只需要非常密切地重现
NHibernate.Linq.EagerFetchingExtensionMethods
上课。
需要一个界面:IFetchRequest
,实现FetchRequest
的类IFetchRequest
和实现扩展方法的静态类EagerFetch
。这是NHibernate.Linq.EagerFetchingExtensionMethods
类的一种克隆。
只需定义:
public interface IFetchRequest<TQueried, TFetch> : IOrderedQueryable<TQueried> {}
模仿NHibernate.Linq.INhFetchRequest<TQueried, TFetch>
然后定义一个实现:
public class FetchRequest<TQueried, TFetch> : IFetchRequest<TQueried, TFetch> {
#region IEnumerable<TQueried> Members
public IEnumerator<TQueried> GetEnumerator(){
return NhFetchRequest.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return NhFetchRequest.GetEnumerator();
}
#endregion
#region IQueryable Members
public Type ElementType {
get { return NhFetchRequest.ElementType; }
}
public System.Linq.Expressions.Expression Expression {
get { return NhFetchRequest.Expression; }
}
public IQueryProvider Provider {
get { return NhFetchRequest.Provider; }
}
#endregion
public FetchRequest(INhFetchRequest<TQueried, TFetch> nhFetchRequest){
NhFetchRequest = nhFetchRequest;
}
public INhFetchRequest<TQueried, TFetch> NhFetchRequest { get; private set; }
}
这个只是拥有一个nHibernate实现,并将每个方法转发给该成员。
最后:
public static class EagerFetch {
/*
replacing methods from NHibernate.Linq.EagerFetchingExtensionMethods
private static INhFetchRequest<TOriginating, TRelated> CreateFluentFetchRequest<TOriginating, TRelated>(MethodInfo currentFetchMethod, IQueryable<TOriginating> query, LambdaExpression relatedObjectSelector);
public static INhFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector);
public static INhFetchRequest<TOriginating, TRelated> FetchMany<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector);
public static INhFetchRequest<TQueried, TRelated> ThenFetch<TQueried, TFetch, TRelated>(this INhFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, TRelated>> relatedObjectSelector);
public static INhFetchRequest<TQueried, TRelated> ThenFetchMany<TQueried, TFetch, TRelated>(this INhFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector);
*/
public static IFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector){
var fetch = EagerFetchingExtensionMethods.Fetch(query, relatedObjectSelector);
return new FetchRequest<TOriginating, TRelated>(fetch);
}
public static IFetchRequest<TOriginating, TRelated> FetchMany<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector){
var fecth = EagerFetchingExtensionMethods.FetchMany(query, relatedObjectSelector);
return new FetchRequest<TOriginating, TRelated>(fecth);
}
public static IFetchRequest<TQueried, TRelated> ThenFetch<TQueried, TFetch, TRelated>(this IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, TRelated>> relatedObjectSelector){
var impl = query as FetchRequest<TQueried, TFetch>;
var fetch = EagerFetchingExtensionMethods.ThenFetch(impl.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
public static IFetchRequest<TQueried, TRelated> ThenFetchMany<TQueried, TFetch, TRelated>(this IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector){
var impl = query as FetchRequest<TQueried, TFetch>;
var fetch = EagerFetchingExtensionMethods.ThenFetchMany(impl.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
}
答案 1 :(得分:1)
基于guido的回答,这里是一个断开所有NHibernate依赖关系与存储库接口的连接。虽然如果你想利用很多NHibernate特定的功能,但可能不是一个很好的技术;然后引用NHibernate.dll可能更合适。
首先是接口:
public interface IFetchableQueryable<TQueried> : IQueryable<TQueried> {
IFetchRequest<TQueried, TRelated> Fetch<TRelated>(Expression<Func<TQueried, TRelated>> relatedObjectSelector);
IFetchRequest<TQueried, TRelated> FetchMany<TRelated>(Expression<Func<TQueried, IEnumerable<TRelated>>> relatedObjectSelector);
}
public interface IFetchRequest<TQueried, TFetch> : IOrderedQueryable<TQueried> {
IFetchRequest<TQueried, TRelated> ThenFetch<TRelated>(Expression<Func<TFetch, TRelated>> relatedObjectSelector);
IFetchRequest<TQueried, TRelated> ThenFetchMany<TRelated>(Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector);
}
然后执行:
public class FetchableQueryable<TQueried> : IFetchableQueryable<TQueried> {
public FetchableQueryable(IQueryable<TQueried> query) {
this.Query = query;
}
public IQueryable<TQueried> Query { get; private set; }
#region IEnumerable<TQueried> Members
public IEnumerator<TQueried> GetEnumerator() {
return this.Query.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return this.Query.GetEnumerator();
}
#endregion
#region IQueryable Members
public Type ElementType {
get { return this.Query.ElementType; }
}
public Expression Expression {
get { return this.Query.Expression; }
}
public IQueryProvider Provider {
get { return this.Query.Provider; }
}
#endregion
#region IFetchableQueryable<TQueried> Members
public IFetchRequest<TQueried, TRelated> Fetch<TRelated>(Expression<Func<TQueried, TRelated>> relatedObjectSelector) {
return new FetchRequest<TQueried, TRelated>(this.Query.Fetch<TQueried, TRelated>(relatedObjectSelector));
}
public IFetchRequest<TQueried, TRelated> FetchMany<TRelated>(Expression<Func<TQueried, IEnumerable<TRelated>>> relatedObjectSelector) {
return new FetchRequest<TQueried, TRelated>(this.Query.FetchMany<TQueried, TRelated>(relatedObjectSelector));
}
#endregion
}
public class FetchRequest<TQueried, TFetch> : IFetchRequest<TQueried, TFetch> {
public FetchRequest(INhFetchRequest<TQueried, TFetch> nhFetchRequest) {
NhFetchRequest = nhFetchRequest;
}
public INhFetchRequest<TQueried, TFetch> NhFetchRequest { get; private set; }
#region IEnumerable<TQueried> Members
public IEnumerator<TQueried> GetEnumerator() {
return NhFetchRequest.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return NhFetchRequest.GetEnumerator();
}
#endregion
#region IQueryable Members
public Type ElementType {
get { return NhFetchRequest.ElementType; }
}
public System.Linq.Expressions.Expression Expression {
get { return NhFetchRequest.Expression; }
}
public IQueryProvider Provider {
get { return NhFetchRequest.Provider; }
}
#endregion
#region IFetchRequest<TQueried,TFetch> Members
public IFetchRequest<TQueried, TRelated> Fetch<TRelated>(Expression<Func<TQueried, TRelated>> relatedObjectSelector) {
var fetch = EagerFetchingExtensionMethods.Fetch(this.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
public IFetchRequest<TQueried, TRelated> FetchMany<TRelated>(Expression<Func<TQueried, IEnumerable<TRelated>>> relatedObjectSelector) {
var fecth = EagerFetchingExtensionMethods.FetchMany(this.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fecth);
}
public IFetchRequest<TQueried, TRelated> ThenFetch<TRelated>(Expression<Func<TFetch, TRelated>> relatedObjectSelector) {
var fetch = EagerFetchingExtensionMethods.ThenFetch(this.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
public IFetchRequest<TQueried, TRelated> ThenFetchMany<TRelated>(Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector) {
var fetch = EagerFetchingExtensionMethods.ThenFetchMany(this.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
#endregion
}
答案 2 :(得分:0)
我为解决这个问题所做的工作是在我的存储库中创建public virtual
函数以EagerlyFetch
我的对象。然后,在我的单元测试中,我使用该Stub代替传递除我的EagerlyFetch
方法之外的所有内容,该方法只返回一个列表。以下是我所做的一个例子:
public class PersistenceBroker
{
private ISession _session;
public IQueryable<T> Query<T>()
{
return Session.Query<T>();
}
.
.
.
}
public class PersonRepository : IPersonRepository
{
private PersistenceBroker _persistenceBroker;
public List<Person> PeopeWhoLiveIn(string city)
{
var people = _persistenceBroker.Query<Person>()
Where(x => x.City == city)l
return EagerlyFetch(people);
}
public virtual List<Person> EagerlyFetch(IQueryable<Person> people)
{
return people.Fetch(x => x.Mom)
.FetchMany(x => x.Children)
.ToList();
}
}
然后在我的测试中,我只提供一个PersonRepositoryStub:
public class PersonRepositoryStub : PersonRepository
{
public override List<Person> EagerlyFetch(IQueryable<Person> people)
{
return people.ToList();
}
}
这可以替代上面的一些答案(我还没试过),但这对我有用。
干杯,
列维
答案 3 :(得分:0)
或者将测试数据IEnumerable包装在实现IFutureValue的存根中(NB使用remotion http://relinq.codeplex.com/)。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NHibernate;
using Remotion.Linq;
namespace SomeNameSpaceNearYou
{
public class NhStubQueryable<TData> : QueryableBase<TData>, IEnumerable<TData>, IFutureValue<TData>
{
private readonly IEnumerable<TData> _enumerable;
public NhStubQueryable(IEnumerable<TData> enumerable)
: base(new NhStubQueryProvider())
{
_enumerable = enumerable;
}
/// <summary>
/// This constructor is called by Provider.CreateQuery().
/// </summary>
//public NhStubQueryable(NhStubQueryProvider<TData> provider, Expression expression)
public NhStubQueryable(NhStubQueryProvider provider, Expression expression)
: base(provider, expression)
{
if (provider == null)
{
throw new ArgumentNullException("provider");
}
if (expression == null)
{
throw new ArgumentNullException("expression");
}
if (!typeof(IQueryable<TData>).IsAssignableFrom(expression.Type))
{
throw new ArgumentOutOfRangeException("expression");
}
}
#endregion
#region Enumerators
IEnumerator<TData> IEnumerable<TData>.GetEnumerator()
{
if (_enumerable != null)
return _enumerable.GetEnumerator();
return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
if (_enumerable != null)
return _enumerable.GetEnumerator();
return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
}
public new IEnumerator<TData> GetEnumerator()
{
if (_enumerable != null)
return _enumerable.GetEnumerator();
return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
}
#endregion
public IEnumerable Enumerable { get { return _enumerable; } }
public TData Value { get { return this.FirstOrDefault(); } }
}
public class NhStubFutureValue<TData> : IFutureValue<TData>
{
public NhStubFutureValue(TData value)
{
Value = value;
}
public TData Value { get; private set; }
}
}
(我没有写这个,归功于比我更熟练的同事)