2011-01-26 11 views
14

estoy usando un repositorio genérico que expone un IQueryable<T> así:¿Cómo envuelvo .Fetch y .ThenFetch de Linq2NHibernate dentro de mi repositorio abstracto?

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

puedo consulta como esta:

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

Sin embargo, si una entidad tiene T padre y abuelo y quiero cargarlos con entusiasmo, tengo que hacer esto:

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

esto funciona, pero .Fetch y .ThenFetch son los dos métodos de extensión específica Linq2Nhibernate, que están produciendo dos problemas:

  1. tengo que incluir una declaración using NHibernate.Linq; en la parte superior de mi archivo. Sin embargo, en el momento en que hago esta consulta, debe ser independiente de la implementación.

  2. cuando intento de probar la unidad de esto, los métodos y .Fetch.ThenFetch fallar cuando se ejecuta en contra de la IQueryable<T> que mi repositorio proporciona simulacro.

¿Cómo puedo envolver estos dentro de mi interfaz IRepository<T>, o en el interior de algunos métodos de extensión genéricos?

Actualización:

Hasta ahora todo lo que ocurre es que añadir esto a mi interfaz de repositorio:

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

... y esto a mi aplicación repositorio 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); 
} 

El consumo de esta API ahora hace esto:

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

Pero esto carece de la buena sintaxis del método de extensión que prefiero. Estoy buscando algo más cercano a la sintaxis .Fetch y .ThenFetch.

Respuesta

13

Después de algunas investigaciones creo que tengo una receta: simplemente siga de cerca la implementación NHibernate.Linq para tener su propia implementación y evitar una dependencia NHibernate.Linq explícita en su código de cliente. Solo necesita reproducir muy de cerca NHibernate.Linq.EagerFetchingExtensionMethods clase.

Lleva una interfaz: IFetchRequest, una clase FetchRequest implementando IFetchRequest y una clase estática EagerFetch implementando métodos de extensión. Este es un tipo de clon de la clase NHibernate.Linq.EagerFetchingExtensionMethods.

apenas define:

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

que imita NHibernate.Linq.INhFetchRequest<TQueried, TFetch>

a continuación, definir una implementación:

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

Ésta simplemente sostiene una aplicación NHibernate delante y hacia todos los métodos a ese miembro.

Por último:

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

Eso es realmente bueno, pero ahora lo necesito para implementar IFetchRequest que trabaja con LINQ a objetos? – Sly

+0

Finalmente encontré una solución a esto. ¡Gracias! – Chris

+0

@Chris - ¿Podría compartir qué solución encontró? – Sam

1

Sobre la base de la respuesta de Guido, aquí hay uno que desconecta todas las dependencias de NHibernate desde la interfaz de repositorio. Un poco de placa de caldera, aunque probablemente no es una buena técnica si desea utilizar una gran cantidad de funcionalidad específica de NHibernate; luego, hacer referencia al NHibernate.dll podría ser más apropiado.

Primero las interfaces:

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

Y entonces la aplicación:

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

Lo que he hecho para trabajar alrededor de esto es crear una función public virtual en mi repositorio para EagerlyFetch mi objeto. Luego, en las pruebas de mi unidad, uso ese Stub que pasa a través de todo, excepto el método EagerlyFetch, que simplemente devuelve una lista. Aquí está un ejemplo de lo que he hecho:

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

Y luego, en mis pruebas, simplemente proporcionar una PersonRepositoryStub:

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

Esta sería una alternativa a algunas de las respuestas anteriores (que No lo he intentado), pero esto funciona para mí.

Saludos,

Levi

0

Alternativamente envuelven sus datos de prueba IEnumerable en un talón que implementa IFutureValue (Nota: este utiliza remoción 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; } 
    } 
} 

(yo no escribo esto, el crédito a un compañero mucho más hábil que yo)

Cuestiones relacionadas