3

Currently we are using a generic repository for all our entities that exposes an IQueryable (using NH3 linq support) that is then used by our service layer to construct specific queries.

I now need to eagerly load an association. Is there any way I can expose an IQueryable and pass in optional fetch expressions? The problem I see is that Fetch must come last in the expression (as per http://mikehadlow.blogspot.com/2010/08/nhibernate-linq-eager-fetching.html).

I'm curious as to how others have achieved this.

I did consider perhaps passing Linq specifications to the repository so that these could evaluated prior to calling Fetch. I would however still need some way of passing in the Fetch expressions.

Thanks Ben

Ben Foster
  • 34,340
  • 40
  • 176
  • 285

3 Answers3

3

I use overloads of my FindOne and FindAll repository calls to achieve this..something like:

Function FindOne(ByVal spec As ILinqSpecification(Of T)) As T
Function FindOne(ByVal spec As ILinqSpecification(Of T), ByVal strategy As IFetchingStrategy(Of T)) As T
Function FindAll(ByVal spec As ILinqSpecification(Of T)) As IQueryable(Of T)
Function FindAll(ByVal spec As ILinqSpecification(Of T), ByVal strategy As IFetchingStrategy(Of T)) As IQueryable(Of T)

etc..

Perhaps not the cleanest approach, but it does the job. I'm not certain if this is still an issue with the trunk linq provider or not, but I can also decide whether or not to apply the distinct result transformer to my results in the FindAll scenarios based upon whether or not my fetching strategy contains a collection.

My specification and fetching strategy implementations are based upon those available in the ncommon project.

For reference, my full generic "read-only" repository interface is as follows:

Public Interface IReadOnlyRepositoryWithTypedId(Of T As IEntityWithTypedId(Of IdT), IdT)

    Function LoadById(ByVal id As IdT) As T
    Function GetById(ByVal id As IdT) As T
    Function FindOne(ByVal spec As ILinqSpecification(Of T)) As T
    Function FindOne(ByVal spec As ILinqSpecification(Of T), ByVal strategy As IFetchingStrategy(Of T)) As T
    Function GetCount() As Integer
    Function GetCount(ByVal spec As ILinqSpecification(Of T)) As Integer
    Function HasAny(ByVal spec As ILinqSpecification(Of T)) As Boolean
    Function FindAll(ByVal spec As ILinqSpecification(Of T)) As IQueryable(Of T)
    Function FindAll(ByVal spec As ILinqSpecification(Of T), ByVal strategy As IFetchingStrategy(Of T)) As IQueryable(Of T)
    Function FindAll() As IQueryable(Of T)
    Function FindAll(ByVal strategy As IFetchingStrategy(Of T)) As IQueryable(Of T)

End Interface
DanP
  • 6,310
  • 4
  • 40
  • 68
  • Are you using LinqSpecs? Could you perhaps post the full signature of these methods? – Ben Foster Nov 12 '10 at 14:10
  • @Ben - I have updated my answer accordingly, let me know if you need any further details. – DanP Nov 12 '10 at 14:39
  • 1
    Another alternate approach you could consider: http://fabiomaulo.blogspot.com/2010/07/enhanced-query-object.html I'm interested in trying this concept out in future projects. – DanP Nov 12 '10 at 15:13
2

What I did was something like this (sorry, C#): First, the interface:

IQueryable<T> All<T>(params Expression<Func<T, Object>> [] fetchPaths);

As for the implementation:

public IQueryable<T> All<T>(params Expression<Func<T, Object>> [] fetchPaths)
{
    var queryable = this.session.Query<T>();

    foreach (var fetchPath in fetchPaths)
    {
        queryable = queryable.Fetch(fetchPath);
    }

    return queryable;
}
Ricardo Peres
  • 13,724
  • 5
  • 57
  • 74
  • 1
    thanks your solution, it was very good I just applied linq leaving it so (fetchPaths .Aggregate (Session.Query (). Where (expression), (current, fetchPath) => current.Fetch (fetchPath))) – Marco Vinicius Soares Dalalba Sep 21 '18 at 13:28
2

The solution I came up with in the end was adding the following to my generic repository interface:

public IEnumerable<T> FindAll<TRelated>(Specification<T> specification, Expression<Func<T, TRelated>> fetchExpression);

The NHibernate implementation was:

public IEnumerable<Product> FindAll<TRelated>(Specification<Product> specification, Expression<Func<Product, TRelated>> fetchExpression) {
return session.Query<Product>()
    .Where(specification.IsSatisfiedBy())
        .Fetch(fetchExpression);

}

I'm using Linq Specs (http://linqspecs.codeplex.com).

For full details please see http://blogs.planetcloud.co.uk/mygreatdiscovery/post/Eager-loading-with-NHibernate-LINQ.aspx

However, as Dan pointed out in his comment, this does looks like a better way of abstracting such queries.

Ben Foster
  • 34,340
  • 40
  • 176
  • 285
  • Consider further encapsulation of your fetching strategies; what happens if you need to eagerly fetch 2 referenced entities, for instance? – DanP Nov 12 '10 at 18:09
  • @DanP - good point. I would probably change this to accept a Expression> parameter. – Ben Foster Nov 12 '10 at 22:18
  • Can anyone tell me how I use the method described by Ben Foster? There is only a example for declaration but no call to fetch multiple more than one reference – Timm Bremus Aug 08 '12 at 14:52