Как я могу обернуть Linq2NHibernate это.Fetch и.ThenFetch в моем абстрактный репозиторий?

я использую общий репозиторий, который предоставляет 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, что вызывает две проблемы:

  1. я должен включить using NHibernate.Linq; заявление наверху моего досье. Однако в тот момент, когда я делаю этот запрос, он должен быть агностиком реализации.

  2. когда я пытаюсь проверить это,.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 синтаксис.

4 ответов


после некоторого исследования я думаю, что у меня есть рецепт: просто следуйте внимательно 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);
    }
}

основываясь на ответе Гвидо, вот один, который отключает все зависимости 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
}

что я сделал, чтобы обойти это, чтобы создать public virtual функция в моем репозитории для EagerlyFetch мой объект. Затем в моих модульных тестах я использую этот заглушку, которая проходит через все, кроме моего 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();
    }
}

Это было бы альтернативой некоторым из приведенных выше ответов (которые я не пробовал), но это работает для меня.

спасибо,

Леви


альтернативно оберните тестовые данные 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; }
    }
}

(Я не писал этого, кредит коллеге гораздо более опытным, чем я)