2015-10-22 5 views
2

Мне нравится NHibernate (и NHibernate.Linq). Я не досрочно оптимизирую, но иногда я попаду в очень неприятную проблему N + 1. Рекомендуемое исправление для N + 1 заключается в использовании метода расширения NH Fetch.Nhibernate .Fetch calls fail на издеваемую сессию

Проблема возникает, когда я создаю макет ISession. Я создам List<User> и задаю свой макет, чтобы вернуть список, когда кто-то звонит _session.Query<User>(). Когда я добавляю Fetch вызов запроса (т.е. _session.Query<User>().Fetch(u => u.Address), я получаю следующее сообщение об ошибке: принести

There is no method 'Fetch' on type 'NHibernate.Linq.EagerFetchingExtensionMethods' 
that matches the specified arguments 

NHibernate-х принимает обычный старый IQueryable<T> но пытается бросить его в качестве конкретных реализаций NH и терпит неудачу, если он не может .

Я бы очень хотел, Fetch не ошибка, если она вызвана на реализацию без NH (т.е. списка) и просто игнорируются, так что я все еще могу использовать его в своих модульных тестах. Помогите!

ответ

1

Ну , Я попытался реализовать это сам, но, слава богу, я нашел человека, который уже беготня.

http://mycodinglife.blog.com/2013/06/10/fetch-good-boy-now-play-nice-with-my-unit-tests/#

Единственное, что вам нужно сделать, это вызов EagerlyFetch вместо просто Fetch.

Я скопировал соответствующий код ниже, потому что в его блоге уже имеется достаточное количество ошибок http 500 и проблем с CSS. Я не думаю, что это поддерживается.

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
using NHibernate.Linq; 
using Remotion.Linq; 

namespace LittleFish.Persistence.Extensions 
{ 
    /// <summary> 
    /// Provides extension method wrappers for NHibernate methods 
    /// to allow consuming source code to avoid "using" NHibernate. 
    /// </summary> 
    public static class NHibernateExtensions 
    { 
     /// <summary> 
     /// Eager-loads a projection of the specified queryable, 
     /// referencing a mapped child object. 
     /// </summary> 
     public static IFetchRequest<T, TRel> EagerlyFetch<T, TRel>(
      this IQueryable<T> queryable, 
      Expression<Func<T, TRel>> expression) 
     { 
      if (queryable is QueryableBase<T>) 
       return FetchHelper.Create(queryable.Fetch(expression)); 
      else 
       return FetchHelper.CreateNonNH<T, TRel>(queryable); 
     } 

     /// <summary> 
     /// Eager-loads a second-level projection of the specified queryable, 
     /// referencing a mapped child of the first eager-loaded child. 
     /// </summary> 
     public static IFetchRequest<T, TRel2> ThenEagerlyFetch<T, TRel, TRel2>(
      this IFetchRequest<T, TRel> queryable, 
      Expression<Func<TRel, TRel2>> expression) 
     { 
      if (queryable is QueryableFetchHelper<T, TRel>) 
       return FetchHelper.CreateNonNH<T, TRel2>(queryable); 
      else 
       return FetchHelper.Create(queryable.ThenFetch(expression)); 
     } 

     /// <summary> 
     /// Eager-loads a projection of the specified queryable, 
     /// referencing a mapped child object. 
     /// </summary> 
     public static IFetchRequest<T, TRel> EagerlyFetchMany<T, TRel>(
      this IQueryable<T> queryable, 
      Expression<Func<T, IEnumerable<TRel>>> expression) 
     { 
      if(queryable is QueryableBase<T>) 
       return FetchHelper.Create(queryable.FetchMany(expression)); 
      else 
       return FetchHelper.CreateNonNH<T, TRel>(queryable); 
     } 

     /// <summary> 
     /// Eager-loads a second-level projection of the specified queryable, 
     /// referencing a mapped child of the first eager-loaded child. 
     /// </summary> 
     public static IFetchRequest<T, TRel2> ThenEagerlyFetchMany 
      <T, TRel, TRel2>(
      this IFetchRequest<T, TRel> queryable, 
      Expression<Func<TRel, IEnumerable<TRel2>>> expression) 
     { 
      if (queryable is QueryableFetchHelper<T, TRel>) 
       return FetchHelper.CreateNonNH<T, TRel2>(queryable); 
      else 
       return FetchHelper.Create(queryable.ThenFetchMany(expression)); 
     } 
    } 

    /// <summary> 
    /// Provides a wrapper for NHibernate's FetchRequest interface, 
    /// so libraries that run eager-loaded queries don't have to reference 
    /// NHibernate assemblies. 
    /// </summary> 
    public interface IFetchRequest<TQuery, TFetch> : 
     INhFetchRequest<TQuery, TFetch> 
    { 
    } 

    internal class NhFetchHelper<TQuery, TFetch> : IFetchRequest<TQuery, TFetch> 
    { 
     private readonly INhFetchRequest<TQuery, TFetch> realFetchRequest; 

     //this is the real deal for NHibernate queries 
     internal NhFetchHelper(INhFetchRequest<TQuery, TFetch> realFetchRequest) 
     { 
      this.realFetchRequest = realFetchRequest; 
     } 

     public IEnumerator<TQuery> GetEnumerator() 
     { 
      return (realFetchRequest).GetEnumerator(); 
     } 

     IEnumerator IEnumerable.GetEnumerator() 
     { 
      return (realFetchRequest).GetEnumerator(); 
     } 

     public Expression Expression 
     { 
      get { return (realFetchRequest).Expression; } 
     } 

     public Type ElementType 
     { 
      get { return (realFetchRequest).ElementType; } 
     } 

     public IQueryProvider Provider 
     { 
      get { return (realFetchRequest).Provider; } 
     } 
    } 

    internal class QueryableFetchHelper<TQuery, TFetch> : 
     IFetchRequest<TQuery, TFetch> 
    { 
     private readonly IQueryable<TQuery> queryable; 

     //for use against non-NH datastores 
     internal QueryableFetchHelper(IQueryable<TQuery> queryable) 
     { 
      this.queryable = queryable; 
     } 


     public IEnumerator<TQuery> GetEnumerator() 
     { 
      return (queryable).GetEnumerator(); 
     } 

     IEnumerator IEnumerable.GetEnumerator() 
     { 
      return (queryable).GetEnumerator(); 
     } 


     public Expression Expression 
     { 
      get { return (queryable).Expression; } 
     } 

     public Type ElementType 
     { 
      get { return (queryable).ElementType; } 
     } 

     public IQueryProvider Provider 
     { 
      get { return (queryable).Provider; } 
     } 
    } 

    /// <summary> 
    /// The static "front door" to FetchHelper, with generic factories allowing 
    /// generic type inference. 
    /// </summary> 
    internal static class FetchHelper 
    { 
     public static NhFetchHelper<TQuery, TFetch> Create<TQuery, TFetch>(
      INhFetchRequest<TQuery, TFetch> nhFetch) 
     { 
      return new NhFetchHelper<TQuery, TFetch>(nhFetch); 
     } 

     public static NhFetchHelper<TQuery, TFetch> Create<TQuery, TFetch>(
      IFetchRequest<TQuery, TFetch> nhFetch) 
     { 
      return new NhFetchHelper<TQuery, TFetch>(nhFetch); 
     } 

     public static IFetchRequest<TQuery, TRel> CreateNonNH<TQuery, TRel>(
      IQueryable<TQuery> queryable) 
     { 
      return new QueryableFetchHelper<TQuery, TRel>(queryable); 
     } 
    } 
} 
0

Fetch является метод расширения, который исходит от NHibernate.Linq.EagerFetchingExtensionMethods, и поэтому Вы не можете дразнить его. Если вы принимаете модификацию исходного производственного кода, вы можете использовать обертку. Wrapper - это код, который вы будете дополнительно высмеивать!

Вместо вызова Fetch беглом способом (query.Fetch(...)), Вы можете позвонить обертку и ввести запрос в качестве ссылки:

NHibernateExtensionsWrapper.Fetch(query, x => x.ChildTable).ToList(); 

Как реализовать эту обертку?

public class NHibernateExtensionsWrapper : INHibernateExtensionsWrapper 
{ 
    public INhFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(IQueryable<TOriginating> query, 
     Expression<Func<TOriginating, TRelated>> relatedObjectSelector) 
    { 
     return query.Fetch(relatedObjectSelector); 
    } 
} 

Как реализовать обертки?

public class NHibernateExtensionsWrapperMock : INHibernateExtensionsWrapper 
{ 
    public INhFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector) 
    { 
     return (INhFetchRequest<TOriginating, TRelated>) new NhFetchRequest<TOriginating, TRelated>(query); 
    } 

    private class NhFetchRequest<TOriginating, TRelated> : INhFetchRequest<TOriginating, TRelated> 
    { 
     private readonly IQueryable<TOriginating> _query; 

     public NhFetchRequest(IQueryable<TOriginating> query) 
     { 
      _query = query; 
     } 

     public IEnumerator<TOriginating> GetEnumerator() 
     { 
      return _query.GetEnumerator(); 
     } 

     IEnumerator IEnumerable.GetEnumerator() 
     { 
      return GetEnumerator(); 
     } 

     public Expression Expression => _query.Expression; 

     public Type ElementType => _query.ElementType; 

     public IQueryProvider Provider => _query.Provider; 
    } 
} 
Смежные вопросы