2010-11-12 7 views
3

Actuellement, nous utilisons un référentiel générique pour toutes nos entités qui expose un IQueryable (en utilisant le support linq NH3) qui est ensuite utilisé par notre couche de service pour construire des requêtes spécifiques.NHibernate Linq Eager Chargement avec référentiel générique

J'ai maintenant besoin de charger avec impatience une association. Y a-t-il un moyen d'exposer un IQueryable et de passer dans des expressions d'extraction facultatives? Le problème que je vois est que Fetch doit venir en dernier dans l'expression (selon http://mikehadlow.blogspot.com/2010/08/nhibernate-linq-eager-fetching.html).

Je suis curieux de savoir comment les autres ont réalisé cela.

J'ai effectivement envisagé de transmettre les spécifications Linq au référentiel afin que celles-ci puissent être évaluées avant d'appeler Fetch. J'aurais quand même besoin d'un moyen de passer dans les expressions Fetch.

Merci Ben

Répondre

3

J'utilise de mon FindOne surcharge et FindAll dépôt appelle à réaliser this..something comme:

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 ..

Peut-être pas la plus propre approche, mais ça fait le boulot. Je ne suis pas certain que ce soit toujours un problème avec le fournisseur linq du trunk ou non, mais je peux également décider d'appliquer ou non le transformateur de résultat distinct à mes résultats dans les scénarios FindAll selon que ma stratégie de récupération contient ou non collection. Mes implémentations de spécification et de stratégie de récupération sont basées sur celles disponibles dans le projet ncommon.

Pour référence, toute mon interface générique de référentiel « en lecture seule » est la suivante:

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 
+0

Utilisez-vous LinqSpecs? Pourriez-vous poster la signature complète de ces méthodes? –

+0

@Ben - J'ai mis à jour ma réponse en conséquence, faites-moi savoir si vous avez besoin de plus de détails. – DanP

+1

Une autre approche alternative que vous pourriez envisager: http://fabiomaulo.blogspot.com/2010/07/enhanced-query-object.html Je suis intéressé à essayer ce concept dans de futurs projets. – DanP

2

La solution que je suis venu avec la fin a été ajoutant ce qui suit à mon interface référentiel générique:

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

La mise en œuvre a été NHibernate:

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

}J'utilise Linq Specs (http://linqspecs.codeplex.com).

Pour plus de détails voir s'il vous plaît http://blogs.planetcloud.co.uk/mygreatdiscovery/post/Eager-loading-with-NHibernate-LINQ.aspx

Cependant, comme Dan a souligné dans son commentaire, this-t ressemble à une meilleure façon d'abstraire ces requêtes.

+0

Pensez à encapsuler davantage vos stratégies de récupération; Que se passe-t-il si vous avez besoin de récupérer 2 entités référencées, par exemple? – DanP

+0

@DanP - bon point. Je changerais probablement ceci pour accepter un paramètre Expression >. –

+0

ou un tableau de param :) – DanP

1

Ce que je l'ai fait était quelque chose comme ça (désolé, C#): Tout d'abord, l'interface:

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

En ce qui concerne la mise en œuvre:

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