Les appels Nhibernate .Fetch échouent sur une session simulée

J’aime NHibernate (et NHibernate.Linq). Je n’optimise pas prématurément, mais parfois, je tombe sur un problème vraiment vilain N + 1. Le correctif recommandé pour le N + 1 consiste à utiliser la méthode d’extension Fetch de NH.

Le problème se pose lorsque je crée un simulacre de ISession . Je vais créer une List et configurer ma maquette pour renvoyer la liste chaque fois que quelqu’un appelle _session.Query() . Lorsque j’ajoute un appel Fetch à la requête (c’est-à-dire _session.Query().Fetch(u => u.Address) , le message d’erreur suivant s’affiche:

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

La récupération de NHibernate accepte un IQueryable mais essaie de le convertir en une implémentation spécifique de NH et échoue s’il ne le peut pas.

J’aimerais vraiment que Fetch ne se Fetch pas s’il est appelé sur une implémentation non-NH (c’est-à-dire une liste) et qu’il soit simplement ignoré afin que je puisse toujours l’utiliser dans mes tests unitaires. Aidez-moi!

Eh bien, j’ai essayé d’appliquer cela moi-même, mais Dieu merci, j’ai trouvé quelqu’un qui avait déjà fait les démarches.

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

La seule chose à faire est d’appeler EagerlyFetch au lieu de simplement Fetch .

J’ai copié le code correspondant ci-dessous car son blog contient déjà pas mal d’erreurs http 500 et de problèmes CSS. Je ne pense pas qu’il soit maintenu.

 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 { ///  /// Provides extension method wrappers for NHibernate methods /// to allow consuming source code to avoid "using" NHibernate. ///  public static class NHibernateExtensions { ///  /// Eager-loads a projection of the specified queryable, /// referencing a mapped child object. ///  public static IFetchRequest EagerlyFetch( this IQueryable queryable, Expression> expression) { if (queryable is QueryableBase) return FetchHelper.Create(queryable.Fetch(expression)); else return FetchHelper.CreateNonNH(queryable); } ///  /// Eager-loads a second-level projection of the specified queryable, /// referencing a mapped child of the first eager-loaded child. ///  public static IFetchRequest ThenEagerlyFetch( this IFetchRequest queryable, Expression> expression) { if (queryable is QueryableFetchHelper) return FetchHelper.CreateNonNH(queryable); else return FetchHelper.Create(queryable.ThenFetch(expression)); } ///  /// Eager-loads a projection of the specified queryable, /// referencing a mapped child object. ///  public static IFetchRequest EagerlyFetchMany( this IQueryable queryable, Expression>> expression) { if(queryable is QueryableBase) return FetchHelper.Create(queryable.FetchMany(expression)); else return FetchHelper.CreateNonNH(queryable); } ///  /// Eager-loads a second-level projection of the specified queryable, /// referencing a mapped child of the first eager-loaded child. ///  public static IFetchRequest ThenEagerlyFetchMany ( this IFetchRequest queryable, Expression>> expression) { if (queryable is QueryableFetchHelper) return FetchHelper.CreateNonNH(queryable); else return FetchHelper.Create(queryable.ThenFetchMany(expression)); } } ///  /// Provides a wrapper for NHibernate's FetchRequest interface, /// so libraries that run eager-loaded queries don't have to reference /// NHibernate assemblies. ///  public interface IFetchRequest : INhFetchRequest { } internal class NhFetchHelper : IFetchRequest { private readonly INhFetchRequest realFetchRequest; //this is the real deal for NHibernate queries internal NhFetchHelper(INhFetchRequest realFetchRequest) { this.realFetchRequest = realFetchRequest; } public IEnumerator 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 : IFetchRequest { private readonly IQueryable queryable; //for use against non-NH datastores internal QueryableFetchHelper(IQueryable queryable) { this.queryable = queryable; } public IEnumerator 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; } } } ///  /// The static "front door" to FetchHelper, with generic factories allowing /// generic type inference. ///  internal static class FetchHelper { public static NhFetchHelper Create( INhFetchRequest nhFetch) { return new NhFetchHelper(nhFetch); } public static NhFetchHelper Create( IFetchRequest nhFetch) { return new NhFetchHelper(nhFetch); } public static IFetchRequest CreateNonNH( IQueryable queryable) { return new QueryableFetchHelper(queryable); } } } 

Fetch est une méthode d’extension qui provient de NHibernate.Linq.EagerFetchingExtensionMethods et c’est pourquoi vous ne pouvez pas vous en moquer. Si vous acceptez la modification du code de production d’origine, vous pouvez utiliser un wrapper. Wrapper est le code dont vous vous moquerez davantage!

Au lieu d’appeler Fetch de manière fluide ( query.Fetch(...) ), vous pouvez appeler un wrapper et injecter une requête comme référence:

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

Comment implémenter ce wrapper?

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

Comment implémenter une maquette de wrapper?

 public class NHibernateExtensionsWrapperMock : INHibernateExtensionsWrapper { public INhFetchRequest Fetch(IQueryable query, Expression> relatedObjectSelector) { return (INhFetchRequest) new NhFetchRequest(query); } private class NhFetchRequest : INhFetchRequest { private readonly IQueryable _query; public NhFetchRequest(IQueryable query) { _query = query; } public IEnumerator GetEnumerator() { return _query.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public Expression Expression => _query.Expression; public Type ElementType => _query.ElementType; public IQueryProvider Provider => _query.Provider; } }