5

J'ai mis en œuvre le modèle de cahier des charges avec LINQ comme indiqué ici https://www.packtpub.com/article/nhibernate-3-using-linq-specifications-data-access-layerEn utilisant le chargement désireux avec motif de spécification

Je veux maintenant ajouter la capacité de charge avide et ne suis pas sûr de la meilleure façon de s'y prendre.

La classe de référentiel générique dans l'exemple lié:

public IEnumerable<T> FindAll(Specification<T> specification) 
{ 
    var query = GetQuery(specification); 
    return Transact(() => query.ToList()); 
} 

public T FindOne(Specification<T> specification) 
{ 
    var query = GetQuery(specification); 
    return Transact(() => query.SingleOrDefault()); 
} 

private IQueryable<T> GetQuery(
    Specification<T> specification) 
{ 
    return session.Query<T>() 
    .Where(specification.IsSatisfiedBy()); 
} 

et la mise en œuvre des spécifications:

public class MoviesDirectedBy : Specification<Movie> 
{ 

private readonly string _director; 

public MoviesDirectedBy(string director) 
{ 
    _director = director; 
} 

public override 
    Expression<Func<Movie, bool>> IsSatisfiedBy() 
{ 
    return m => m.Director == _director; 
} 
} 

Cela fonctionne bien, je veux maintenant ajouter la capacité d'être en mesure de charger désireux . Je comprends NHibernate chargement impatient peut être fait en utilisant Fetch sur la requête. Ce que je cherche est d'encapsuler la logique de chargement impatiente dans la spécification ou de la passer dans le référentiel, ainsi que la syntaxe Linq/arbre d'expression nécessaire pour atteindre cet objectif (un exemple de comment cela serait fait).

Répondre

3

serait une solution possible d'étendre la classe de spécification ajouter:

public virtual IEnumerable<Expression<Func<T, object>>> FetchRelated 
{ 
    get 
    { 
     return Enumerable.Empty<Expression<Func<T, object>>>(); 
    } 
} 

Et changer GetQuery à quelque chose comme:

 return specification.FetchRelated.Aggregate(
      session.Query<T>().Where(specification.IsSatisfiedBy()), 
      (current, related) => current.Fetch(related)); 

Maintenant, tout ce que vous devez faire est remplacer FetchRelated en cas de besoin

public override IEnumerable<Expression<Func<Movie, object>>> FetchRelated 
{ 
    get 
    { 
     return new Expression<Func<Movie, object>>[] 
        { 
         m => m.RelatedEntity1, 
         m => m.RelatedEntity2 
        }; 
    } 
} 

Une limitation importante de cette implémentation que je viens d'écrire est t Vous ne pouvez récupérer que les entités directement liées à l'entité racine.

Une amélioration serait de soutenir des niveaux arbitraires (utilisant ThenFetch), qui nécessiteraient des changements dans la façon dont nous travaillons avec les génériques (je object pour permettre la combinaison de différents types d'entités facilement)

+0

C'est certainement une solution, mais c'est un mélange de responsabilités (encapsulation des stratégies d'interrogation et de récupération). C'est pourquoi le modèle de référentiel est faible. – jason

+0

Vrai, mais vous pouvez toujours séparer les stratégies de récupération dans une autre classe ou faire de FetchRelated une propriété qui peut être définie par une couche de service. –

+0

@Diego Mijelshon: Que gagnons-nous de ce niveau d'abstraction supplémentaire? – jason

1

Vous ne voudriez pas mettre l'appel Fetch() dans la spécification, parce que ce n'est pas nécessaire. La spécification est juste pour limiter les données qui peuvent ensuite être partagées à travers de nombreuses parties différentes de votre code, mais ces autres parties pourraient avoir des besoins radicalement différents dans les données qu'ils veulent présenter à l'utilisateur, c'est pourquoi à ces points, vous ajouteriez votre Récupérer des instructions