2

Essayer de faire un dépôt très simple et modèle couche de service ici. (.NET 4, C#, LINQ, bien que cette question soit partiellement agnostique). Remarque: ce n'est que R & D.Conseils Avec référentiel/service Design Pattern couche

Mon objectif est de minimiser la quantité de définitions de méthode dans ma couche de service.

Voici mon contrat Repository:

interface IFooRepository 
{ 
    IEnumerable<Foo> Find(); 
    void Insert(Foo foo); 
    void Update(Foo foo); 
    void Delete(Foo foo); 
} 

Rien de nouveau.

Maintenant, voici ce que im (essayer) d'avoir dans mon contrat de service:

interface IFooDataService 
{ 
    public IEnumerable<Foo> Find(FooSearchArgs searchArgs); 
} 

Essentiellement, tout "Foo" particulier possède de nombreuses propriétés (id, nom, etc.), que je voudrais être capable de chercher.

Donc, je ne veux pas avoir la méthode 1x Trouver pour chaque propriété différente, je veux juste un - de cette façon quand je crée des propriétés supplémentaires je ne doivent modifier les contrats.

Les "FooSearchArgs" est un simple POCO avec toutes les différentes propriétés "Foo" it.

Alors, c'est ce que Im essayant de faire, voici mes questions:

  • Est-ce une mauvaise conception? Si oui, quelles sont les alternatives?
  • Comment puis-je implémenter ce filtrage dans la couche de service? Devrais-je vérifier quelles propriétés de "FooSearchArgs" sont définies, puis continuer à filtrer? (si cela, puis query.where, si cela, query.where, etc) Quelqu'un at-il une idée d'une méthode intelligente d'extension LINQ IEnumerable pour ce faire? (c'est-à-dire repository.WhereMeetsSearchCriteria(fooSearchArgs))

Appréciez l'aide.

Répondre

3

Nous utilisons quelque chose de très similaire. Une chose que vous devez décider est si vous allez exposer IQueryable en dehors du référentiel. Votre méthode find renvoie IEnumerable, qui peut être le IQueryable renvoyé à partir de votre clause when.

L'avantage de retourner le IQueryable est que vous pouvez affiner vos critères à l'extérieur de votre couche dépôt.

repository.Find(predicate).Where(x => x.SomeValue == 1); 

L'expression ne sera compilée que lorsque vous utiliserez les données renvoyées et que l'inconvénient réside ici. Parce que vous ne frappez la base de données que lorsque vous en utilisez les résultats, vous pourriez finir par essayer d'appeler la base de données après la fermeture de votre session (nhibernate) ou la fermeture des connexions.

Ma préférence personnelle est d'utiliser le modèle de spécification où vous transmettez votre méthode find un objet ISpecification est utilisé pour faire la requête.

public interface ISpecification<TCandidate> 
{ 
    IQueryable<TCandidate> GetSatisfyingElements(IQueryable<TCandidate> source); 
} 

public class TestSpecification : ISpecification<TestEntity> 
{ 
    public IQueryable<TestEntity> GetSatisfyingElements(IQueryable<TestEntity> source) 
    { 
     return source.Where(x => x.SomeValue == 2); 
    } 
} 

public class ActiveRecordFooRepository: IFooRepository 
{ 
    ... 

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    { 
     ... 

     return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).ToArray(); 

     ... 
    } 

    public TEntity FindFirst<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    { 
     return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).First(); 
    } 
} 

Une fois la requête est exécutée le dépôt appelle ToArray ou ToList sur le résultat IQueryable retour de la spécification de telle sorte que la requête est évaluée puis il. Bien que cela puisse sembler moins flexible que d'exposer IQueryable, il présente plusieurs avantages.

  1. Les requêtes sont exécutées immédiatement et empêchent un appel à la base de données après la fermeture des sessions. Comme vos requêtes sont maintenant regroupées dans les spécifications, elles sont testables à l'unité.
  2. Les spécifications sont réutilisables, ce qui signifie que vous n'avez pas de duplication de code lorsque vous essayez d'exécuter des requêtes similaires et que les bogues dans les requêtes doivent uniquement être corrigés au même endroit.
  3. Avec le bon type de mise en œuvre, vous pouvez également enchaîner vos spécifications.

repository.Find(
    firstSpecification 
     .And(secondSpecification) 
     .Or(thirdSpecification) 
     .OrderBy(orderBySpecification)); 
+0

Une prise intéressante. Cependant, je préfère utiliser l'exécution différée (LINQ), car elle donne un contrôle beaucoup plus fin sur les requêtes (ce qui me permet de les construire dans la couche de service). Les appels en session fermée ne sont pas un problème car ils utilisent un autre modèle de conception pour combattre cela (ie UnitOfWork). Merci pour la réponse si (+1) - mal lire le schéma de spécification. – RPM1984

+0

Vous pouvez trouver cela intéressant alors: http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/ C'est un exemple de la façon de mettre en œuvre un référentiel ignorant la persistance en utilisant la spécification et l'unité de modèles de travail. – Bronumski

+0

Et celui-ci: http://www.kitchaiyong.net/2009/10/repository-specification-unit-of-work.html. Celui-ci va dans un peu plus de profondeur. – Bronumski

0

passe un Func comme paramètre à votre méthode Find de couche de service, au lieu des FooSearchArgs, une option? Enumerables a une méthode Where (linq) qui prend un Func en paramètre, donc vous pouvez l'utiliser pour filtrer les résultats.

+0

Son option, oui - im expression d'apprentissage encore des arbres/lambdas. Avez-vous une chance de développer votre réponse avec un exemple (pertinent à ma question ci-dessus)? Merci – RPM1984

+0

Cela pourrait être inefficace si vous n'utilisez pas Linq car vous pourriez retourner plus de résultats de la base de données que nécessaire. – Craig

+0

@Craig im utilisant LINQ. Seulement avoir un faux dépôt pour le moment, mais le vrai sera L2SQL ou EF. – RPM1984

Questions connexes