J'ai vu beaucoup de projets qui ont des référentiels qui renvoient des instances de IQueryable
. Cela permet des filtres supplémentaires et le tri peut être effectué sur un IQueryable
autre code, ce qui se traduit par la génération de SQL différent. Je suis curieux de savoir d'où vient ce modèle et si c'est une bonne idée.
Ma plus grande préoccupation est qu’il IQueryable
s’agisse d’une promesse de consulter la base de données quelque temps plus tard, lorsqu’elle est énumérée. Cela signifie qu'une erreur serait renvoyée à l'extérieur du référentiel. Cela pourrait signifier qu'une exception Entity Framework est renvoyée dans une couche différente de l'application.
J'ai également rencontré des problèmes avec plusieurs ensembles de résultats actifs (MARS) dans le passé (en particulier lors de l'utilisation de transactions) et cette approche semble donner lieu à ce que cela se produise plus souvent.
J'ai toujours appelé AsEnumerable
ou ToArray
à la fin de chacune de mes expressions LINQ pour m'assurer que la base de données est touchée avant de quitter le code du référentiel.
Je me demande si le retour IQueryable
pourrait être utile en tant que bloc de construction d'une couche de données. J'ai vu du code assez extravagant avec un référentiel appelant un autre référentiel pour en créer un encore plus volumineux IQueryable
.
la source
where
clause à un différéIQueryable
, il vous suffit d'envoyer ces données par câble, et non l'ensemble du résultat.Réponses:
Renvoyer IQueryable donnera certainement plus de flexibilité aux utilisateurs du référentiel. Il incombe au client de limiter les résultats, ce qui peut naturellement être à la fois un avantage et une béquille.
Du côté positif, vous n'aurez pas besoin de créer des tonnes de méthodes de référentiel (du moins sur cette couche) - GetAllActiveItems, GetAllNonActiveItems, etc. - pour obtenir les données souhaitées. Selon vos préférences, encore une fois, cela pourrait être bon ou mauvais. Vous devrez (/ devriez) définir les contrats de comportement auxquels adhèrent vos implémentations, mais vous en êtes responsable.
Vous pouvez donc placer la logique d'extraction saccadée à l'extérieur du référentiel et la laisser être utilisée comme l'utilisateur le souhaite. Ainsi, exposer IQueryable donne le maximum de flexibilité et permet une interrogation efficace par opposition au filtrage en mémoire, etc., et pourrait réduire la nécessité de créer une tonne de méthodes d'extraction de données spécifiques.
D'autre part, vous avez maintenant donné un fusil de chasse à vos utilisateurs. Ils peuvent faire des choses que vous n’avez peut-être pas prévues (utiliser excessivement .include (), faire de lourdes requêtes et faire du filtrage en mémoire dans leurs implémentations respectives, etc.), ce qui détournerait des contrôles de superposition et de comportement, car vous avez accès total.
Donc, en fonction de l’équipe, de leur expérience, de la taille de l’application, de la superposition en couches et de l’architecture… tout dépend: - \
la source
Expression<Func<Foo,bool>>
méthode à votre méthode. Ces paramètres peuvent être passésWhere
directement à la méthode, permettant ainsi au consommateur de choisir son filtre sans avoir besoin d'un accès direct à IQueryable <Foo>.Exposer IQueryable aux interfaces publiques n'est pas une bonne pratique. La raison est fondamentale: vous ne pouvez pas fournir la réalisation IQueryable telle qu'elle est dite.
L'interface publique est un contrat entre fournisseur et clients. Et dans la plupart des cas, on s'attend à une mise en œuvre complète. IQueryable est un tricheur:
Le modèle de référentiel nous donne une représentation claire par couches et, plus important encore, une indépendance de réalisation. Donc, nous pouvons changer dans la source de données future.
La question est " Devrait-il y avoir une possibilité de substitution de source de données? ".
Si demain une partie des données peut être déplacée vers les services SAP, la base de données NoSQL ou simplement vers les fichiers texte, pouvons-nous garantir une mise en œuvre correcte de l'interface IQueryable?
( plus de bons points sur pourquoi c'est une mauvaise pratique)
la source
De manière réaliste, vous avez trois alternatives si vous souhaitez une exécution différée:
IQueryable
.GetCustomersInAlaskaWithChildren
ci-dessous)IQueryable
interne.Je préfère le troisième (bien que j'aie aidé des collègues à mettre en œuvre le second également), mais il est évident que certaines opérations de configuration et de maintenance sont nécessaires. (T4 à la rescousse!)
Edit : Pour dissiper la confusion qui entoure ce dont je parle, prenons l'exemple suivant, volé à IQueryable: peut tuer votre chien, voler votre femme, tuer votre volonté de vivre, etc.
Dans le cas où vous exposeriez quelque chose comme ceci:
L'API dont je parle vous permettrait d'exposer une méthode qui vous permettrait d'exprimer
GetCustomersInAlaskaWithChildren
(ou toute autre combinaison de critères) de manière flexible, et le référentiel l'exécuterait comme un IQueryable et vous renverrait les résultats. Mais l’important est qu’il s’exécute à l’intérieur de la couche de référentiel, de sorte qu’il profite toujours de l’exécution différée. Une fois que vous avez obtenu les résultats, vous pouvez toujours utiliser LINQ-to-Objects à votre guise.Un autre avantage d’une approche comme celle-ci est que, comme il s’agit de classes POCO, ce référentiel peut vivre derrière un service Web ou WCF; il peut être exposé à AJAX ou à d'autres appelants qui ne connaissent pas LINQ en premier lieu.
la source
IQueryable
sans exposer leIQueryable
lui - même . Comment allez-vous faire cela avec l'API intégrée?Il n'y a vraiment qu'une seule réponse légitime: cela dépend de la manière dont le référentiel doit être utilisé.
À un extrême, votre référentiel est un très fin wrapper autour d'un DBContext afin que vous puissiez injecter un vernis de testabilité autour d'une application basée sur une base de données. Il n'y a vraiment aucune attente du monde à ce que cela puisse être utilisé de manière déconnectée sans un DB compatible avec LINQ, car votre application CRUD n'en aura jamais besoin. Alors pourquoi ne pas utiliser IQueryable? Je préférerais probablement IEnumarable car vous bénéficiez de la plupart des avantages [lisez: exécution différée] et vous ne vous sentez pas aussi sale.
À moins que vous ne soyez assis à cette extrémité, j'essaierais de tirer parti de l'esprit du modèle de référentiel et de renvoyer les collections matérialisées appropriées qui ne impliquent pas une connexion de base de données sous-jacente.
la source
IEnumerable
, il est important de noter que cela((IEnumerable<T>)query).LastOrDefault()
est très différent dequery.LastOrDefault()
(présumerquery
est unIQueryable
). Donc, il n’est logique de revenir queIEnumerable
si vous allez de toute façon parcourir tous les éléments.NO si vous souhaitez effectuer un test / développement basé sur des tests car il n'existe pas de moyen facile de créer un référentiel factice pour tester votre businesslogic (= repository-consumer) indépendamment de la base de données ou d'un autre fournisseur IQueryable.
la source
D'un point de vue purement architectural, IQueryable est une abstraction qui fuit. En général, cela donne trop de puissance à l'utilisateur. Cela étant dit, il y a des endroits où cela a du sens. Si vous utilisez OData, IQueryable facilite extrêmement la fourniture d'un point de terminaison facile à filtrer, à trier, à grouper, etc. Je préfère créer des DTO sur lesquels mes entités sont mappées et IQueryable renvoie le DTO. De cette manière, je filtre mes données et utilise uniquement la méthode IQueryable pour permettre la personnalisation des résultats par le client.
la source
J'ai fait face à un tel choix aussi.
Résumons donc les côtés positifs et négatifs:
Points positifs:
Points négatifs:
Je recommande de ne pas utiliser cette approche. Pensez plutôt à créer plusieurs spécifications et à implémenter des méthodes de référentiel pouvant interroger la base de données à l'aide de celles-ci. Le modèle de spécification est un excellent moyen d'encapsuler un ensemble de conditions.
la source
Je pense qu'exposer l'IQueryable sur votre référentiel est parfaitement acceptable au cours des premières phases de développement. Certains outils d'interface utilisateur peuvent fonctionner directement avec IQueryable et gérer le tri, le filtrage, le regroupement, etc.
Cela étant dit, je pense que le simple fait d'exposer crud sur le référentiel et de l'appeler un jour est irresponsable. Disposer d'opérations utiles et d'assistants de requête pour les requêtes courantes vous permet de centraliser la logique d'une requête tout en exposant une surface d'interface plus petite aux utilisateurs du référentiel.
Pour les premières itérations d'un projet, exposer publiquement IQueryable sur le référentiel permet une croissance plus rapide. Avant la première version complète, je vous recommande de rendre l'opération de requête privée ou protégée et de regrouper les requêtes génériques sous un même toit.
la source
Ce que j'ai vu faire (et ce que je m’implémente moi-même) est le suivant:
Cela vous permet d’avoir la possibilité de faire une
IQueryable.Where(a => a.SomeFilter)
requête sur votreconcreteRepo.GetAll(a => a.SomeFilter)
référentiel sans exposer d’affaires LINQy.la source