11

J'essaie de décider du meilleur modèle pour l'accès aux données dans mon application MVC. Actuellement, après avoir suivi la série de vitrines MVC, j'utilise des référentiels, exposant IQueryable à une couche de service, qui applique ensuite des filtres. Initialement, j'ai utilisé LINQtoSQL par exemple.Les référentiels doivent-ils exposer IQueryable à la couche de service ou effectuer un filtrage dans l'implémentation?

public interface IMyRepository 
{ 
    IQueryable<MyClass> GetAll(); 
} 

Mis en œuvre en:

public class LINQtoSQLRepository : IMyRepository 
{ 
    public IQueryable<MyClass> GetAll() 
    { 
     return from table in dbContext.table 
      select new MyClass 
      { 
       Field1 = table.field1, 
       ... etc. 
      } 
    } 
} 

Filtre pour ID:

public static class TableFilters 
{  
    public static MyClass WithID(this IQueryable<MyClass> qry, string id) 
    { 
     return (from t in qry 
       where t.ID == id 
       select t).SingleOrDefault(); 
    }  
} 

Appelée du service:

public class TableService 
{ 
    public MyClass RecordsByID(string id) 
    { 
     return _repository.GetAll() 
         .WithID(id); 
    } 
} 

je suis tombé sur un problème quand j'expérimenté avec la mise en œuvre du référentiel utilisant Entity Framew ork avec LINQ to Entities. La classe de filtres dans mon projet contient des opérations plus complexes que le "WHERE ... == ..." dans l'exemple ci-dessus, qui, selon moi, nécessite des implémentations différentes selon le fournisseur LINQ. Plus précisément, j'ai besoin d'exécuter une clause SQL "WHERE ... IN ...". Je suis en mesure de mettre en œuvre ce dans la classe de filtre à l'aide:

string[] aParams = // array of IDs 
qry = qry.Where(t => aParams.Contains(t.ID)); 

Cependant, afin d'effectuer ce contre Entity Framework, je dois fournir une solution telle que la BuildContainsExpression qui est lié à l'Entity Framework. Cela signifie que je dois avoir 2 implémentations différentes de ce filtre particulier, en fonction du fournisseur sous-jacent.

J'apprécierais tout conseil sur la façon dont je devrais procéder à partir d'ici. Il m'a semblé que l'exposition d'un IQueryable à partir de mon dépôt me permettrait d'effectuer des filtres indépendamment du fournisseur sous-jacent, ce qui me permettrait de passer d'un fournisseur à l'autre si nécessaire. Cependant le problème que je décris ci-dessus me fait penser que je devrais effectuer tout mon filtrage dans les dépôts et renvoyer IEnumerable, IList ou des classes uniques.

Un grand merci, Matt

+0

Faux lieu. 'BuildContainsExpression' est * entièrement * indépendant de l'EF (il ne dépend que de Linq et d'Expressions). C'est aussi totalement inutile dans EF 4, qui supporte directement 'IEnumerable.Contains'. –

Répondre

14

C'est une question très populaire. Un que je me demande constamment. J'ai toujours pensé qu'il était préférable de renvoyer IEnumerable plutôt que IQueryable à partir d'un référentiel.

Le but d'un référentiel est d'encapsuler l'infrastructure de base de données afin que le client n'ait pas à s'inquiéter de la source de données. Cependant, si vous renvoyez IQueryable, vous êtes à la merci du consommateur pour savoir quel type de requête sera exécuté contre votre base de données et s'il fera quelque chose que le fournisseur LINQ ne supporte pas.

Prendre la pagination par exemple. Disons que vous avez une entité Client et que votre base de données pourrait avoir des centaines de milliers de clients. Quel code préféreriez-vous que votre client écrive?

var customers = repos.GetCustomers().Skip(skipCount).Take(pageSize).ToList(); 

OU

var customers = repos.GetCustomers(pageIndex, pageSize); 

Dans la première approche que vous le rendre impossible le dépôt de limiter le nombre d'enregistrements récupérés à partir de la source de données. En outre, votre client doit calculer le skipCount.

Dans la seconde approche, vous fournissez une interface plus grossière à votre client.Votre référentiel peut maintenant imposer certaines contraintes sur pageSize afin d'optimiser la requête. Vous encapsulez également le calcul du skipCount. Cependant, cela étant dit, dans votre situation, votre client est votre service. Je suppose donc que la question se résume à une séparation des préoccupations. Où est-il préférable d'effectuer une telle logique de validation? Eh bien, cette réponse peut très bien être "dans le service". Mais qu'en est-il de la réponse à "Où est-il préférable de contenir la logique de requête?". Pour moi, la réponse est clairement "The Repository". C'est son domaine d'expertise prévu.