2011-01-26 1 views
14

私はこのようなIQueryable<T>を公開し、一般的なリポジトリを使用しています:Linq2NHibernateの.Fetchと.ThenFetchは、抽象リポジトリ内でどのようにラップするのですか?

public IQueryable<T> AllEntities 
{ 
    get 
    { 
     return session.Query<T>(); 
    } 
} 

私はこのようなクエリを実行することができますTが親や祖父母の実体があり、場合

var results = 
    (from e in repository.AllEntities 
    where e.SomeProperty == "some value" 
    select e).ToList(); 

しかし、私がしたいです私はこれを行う必要があり、熱心にそれらをロード:

var results = 
    (from e in repository.AllEntities 
    where e.SomeProperty == "some value" 
    select e) 
    .Fetch(x => x.Parent) 
    .ThenFetch(x => x.Grandparent) 
    .ToList(); 

これは動作しますが、.Fetchと私は、ファイルの先頭にusing NHibernate.Linq;文を含める必要が

  1. :は二つの問題を引き起こしているLinq2Nhibernate特定の拡張メソッド、両方です。しかし、このクエリを実行している時点では、それは実装に依存しないでください。

  2. これをユニットテストしようとすると、私のモックリポジトリが提供するIQueryable<T>に対して実行すると、.Fetch.ThenFetchメソッドが失敗します。

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の構文に近いものを探しています。

答えて

13

いくつかの調査の後、私はレシピを持っていると思います。独自の実装を持ち、クライアントコードに明示的なNHibernate.Linqの依存関係を避けるためには、ちょうどNHibernate.Linqの実装に従ってください。ちょうど非常によく再現する必要があります NHibernate.Linq.EagerFetchingExtensionMethodsクラス。

インターフェイス:IFetchRequest、クラスFetchRequestIFetchRequestを実装し、静的クラス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; } 
} 

をこの1つは、単純に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); 
    } 
} 
+0

(私は自分自身よりもはるかに多くの熟練した同僚にこの、クレジットを書いていない)が、今私がオブジェクトにLINQで動作IFetchRequestを実装する必要がありますか? – Sly

+0

最後に私はこれに対する解決策を見つけました。ありがとう! – Chris

+0

@Chris - あなたが見つけた解決策を教えてください。 – Sam

1

ビル、ここでリポジトリのインターフェイスからすべてのNHibernateの依存関係を切断1です。ボイラープレートのかなりのビットと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); 
} 

そして:

私はこれが私の EagerlyFetchオブジェクトに私のリポジトリ内 public virtual関数を作成することです回避するために何をやったか
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 
} 
0

。その後、私の単位テストでは、代わりにリストを返す私の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(); 
    } 
} 

これは、上記の回答のいくつかの代替になります(これは私は試していない)が、これは私のために働く。

乾杯、

レヴィ

0

はまた(NBこれはremotion http://relinq.codeplex.com/を使用しています)IFutureValueを実装スタブでIEnumerableをあなたのテストデータをラップします。

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; } 
    } 
} 

本当に良いです

関連する問題