2011-01-26 11 views
14

Używam rodzajowe repozytorium która eksponuje IQueryable<T> takiego:Jak zawinąć Fetch Linq2NHibernate i .ThenFetch wewnątrz mojego abstrakcyjnego repozytorium?

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

mogę zapytać tak:

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

Jednak jeśli T ma rodzica i dziadka podmiot i chcę załadować je chętnie, muszę to zrobić:

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

to działa, ale .Fetch i .ThenFetch są zarówno Linq2Nhibernate specyficzne metody przedłużania, który powoduje dwa problemy:

  1. muszę zawierać using NHibernate.Linq; oświadczenie w górnej części mojego pliku. Jednak w momencie, w którym robię to zapytanie, powinno być ono agnostyczne.

  2. Przy próbie badanej jednostki to sposoby .Fetch i .ThenFetch fail gdy wykonywane przed IQueryable<T> że moja mock repozytorium zapewnia.

Jak mogę owinąć te wnętrze mojego interfejsu IRepository<T> lub wewnątrz pewnych ogólnych metod rozszerzenie?

Aktualizacja:

Jak dotąd wszystko mam wymyślić jest dodać to do mojego repozytorium interfejsu:

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

... i to do mojego wdrożenia repozytorium 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); 
} 

Konsument tego interfejsu API wykonuje teraz:

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(); 

Ale brakuje w nim ładnej metody składni rozszerzeń, którą wolałbym. Szukam czegoś bliższego składni .Fetch i .ThenFetch.

Odpowiedz

13

Po pewnym dochodzeniu, myślę, że mam przepis: po prostu wykonaj dokładnie implementację NHibernate.Linq, aby mieć własną implementację i uniknąć wyraźnej zależności NHibernate.Linq w kodzie klienta. Musisz tylko bardzo dokładnie odtworzyć klasę NHibernate.Linq.EagerFetchingExtensionMethods.

Interfejs: IFetchRequest, klasa FetchRequest implementująca IFetchRequest i klasa statyczna EagerFetch implementująca metody rozszerzeń. To jest rodzaj klona klasy NHibernate.Linq.EagerFetchingExtensionMethods.

Wystarczy zdefiniować:

public interface IFetchRequest<TQueried, TFetch> : IOrderedQueryable<TQueried> {} 

które naśladuje NHibernate.Linq.INhFetchRequest<TQueried, TFetch>

następnie zdefiniować implementację:

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

To po prostu trzyma realizację NHibernate i wysyła każda metoda do tego członka.

Wreszcie:

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

To jest naprawdę dobry, ale teraz trzeba wdrożyć IFetchRequest który współpracuje z LINQ do obiektów? – Sly

+0

W końcu znalazłem rozwiązanie tego problemu. Dzięki! – Chris

+0

@ Chris - Czy mógłbyś podzielić się znalezionym rozwiązaniem? – Sam

1

Opierając się na odpowiedź Guido, tu jest jeden, który odłącza wszystkie zależności NHibernate z interfejsu repozytorium. Całkiem sporo płyty kotła i prawdopodobnie nie jest to dobra technika, jeśli chcesz korzystać z wielu specyficznych funkcji NHibernate; wtedy odwołanie do pliku NHibernate.dll może być bardziej odpowiednie.

Pierwsze interfejsy:

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

a następnie wdrożenia:

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

Co zrobiłem, aby obejść ten jest stworzenie public virtual funkcję w moim repozytorium do EagerlyFetch mojego obiektu. Następnie w moich testach jednostkowych korzystam z tego skrót, który przechodzi przez wszystko poza moją metodą EagerlyFetch, która właśnie zwraca listę. Oto przykład tego, co robiłem:

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

A potem w moich testów, po prostu zapewnić PersonRepositoryStub:

public class PersonRepositoryStub : PersonRepository 
{ 
    public override List<Person> EagerlyFetch(IQueryable<Person> people) 
    { 
     return people.ToList(); 
    } 
} 

będzie to alternatywa dla niektórych z powyższych odpowiedzi (co Nie próbowałem), ale to działa dla mnie.

Cheers,

Levi

0

Alternatywnie owinąć danych testowych IEnumerable w zalążek, który implementuje IFutureValue (NB ten wykorzystuje 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; } 
    } 
} 

(nie piszę tego kredytu do kolegi znacznie bardziej wykwalifikowanych niż ja)

Powiązane problemy