tag:blogger.com,1999:blog-15136575.post2843891068843721432..comments2023-10-17T12:00:16.772+01:00Comments on Code rant: Should my repository expose IQueryable?Mike Hadlowhttp://www.blogger.com/profile/16441901713967254504noreply@blogger.comBlogger38125tag:blogger.com,1999:blog-15136575.post-60831950619291374602016-07-14T20:27:24.103+01:002016-07-14T20:27:24.103+01:00In my project, the domain model classes are quite ...In my project, the domain model classes are quite distinct from the persistence model (ORM) classes. Hence, if my repo returned an IQueryable (of domain objects), I'd have the write the code to translate that into an ORM call. Never going to happen.<br /><br />If my DM and PM were not separate, I'd do what you're suggesting.Chalkyhttps://www.blogger.com/profile/14854435354846173013noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-35940488489867448542016-03-09T20:11:53.654+00:002016-03-09T20:11:53.654+00:00I am currently using a generic Repository pattern ...I am currently using a generic Repository pattern that returns IQueryable and it is a nightmare when you try to unit test a query that projects into a DTO. As a result there are database queries all over the place throughout the code.<br /><br />Linq to Entites (Linq to Sql) and Linq to Objects do different things, especially where Joins and a DTO are concerned. It would be <b>MUCH</b> easier and better to have queries encapsulated in the Repository and add a Repository Test Double.<br /><br />You have to write the queries somewhere...why not keep them in the repository and create a tighter contract between the repository and its consumers?<br /><br />I am working to convert this app to <b>NOT</b> use generic Repositories that return IQueryable.<br />Anonymoushttps://www.blogger.com/profile/14768705491849620918noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-41678896969125318462014-08-10T08:51:42.067+01:002014-08-10T08:51:42.067+01:00Repo pattern is not exclusive of .Net world. If yo...Repo pattern is not exclusive of .Net world. If you have a pattern exposing IQueryable, please dont call it repository. It may work well, but its not tech independant, thus, is not abstraying the data Access and is breaking the definition of repositoryAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-21311100897820786392011-01-07T14:38:49.118+00:002011-01-07T14:38:49.118+00:00N.b. the above should have <T> generic param...N.b. the above should have <T> generic parameters but Blogger stripped them thinking they were HTML tags.James Morcomhttps://www.blogger.com/profile/11883160014225775464noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-64945933174166625412011-01-07T14:11:06.523+00:002011-01-07T14:11:06.523+00:00I've had a thought on this. What if we instead...I've had a thought on this. What if we instead exposed a special enumerable type from our repository (someone will come up with a better name I'm sure):<br /><br />IRepositoryEnumerable : IEnumerable<br /><br />Then we could implement our own extension methods specifically for the things we want our application to be able to do at it's end. I.e.<br /><br />OrderBy(this IRepositoryEnumerable, ...)<br /><br />AsPaged(this IRepositoryEnumerable, ...)<br /><br />so we can make sure the implementation of those specific methods is efficient without giving the application access to potentially "dangerous" Queryable extensions.James Morcomhttps://www.blogger.com/profile/11883160014225775464noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-77921402157372998362010-06-11T11:11:48.939+01:002010-06-11T11:11:48.939+01:00We expose IQueryable through our generic ISession,...We expose IQueryable through our generic ISession, which restricts usage a little.<br /><br />The best thing is that it allows us to easily unit test complex queries.<br /><br />We can just mock our ISession.QueryFor, to return a new List.AsQueryable().<br /><br />You can then simply assert the collection having been run through the query returns what you expect.Anonymoushttps://www.blogger.com/profile/13552045032292142168noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-69402758753405590792010-01-13T06:48:12.514+00:002010-01-13T06:48:12.514+00:00The keyword here is "LINQ-Capable", as t...The keyword here is "LINQ-Capable", as this will not work for anything other than that. Relying on LINQ is a good thing until something better comes along. Besides, i've stumbled upon a few good DAL architectures such as NetTiers which provide custom querying in a very easy and extensible way without relying on LINQ expressions. But besides that point, arguing whether to use IQueryable or not is like arguing whether to use Helper classes or not. You do create a leaky abstraction this way, but it will work as long as its under control and doesn't overflow your repositories. Kind of like Helper classes which make sense as long as they are controlled.Sergehttps://www.blogger.com/profile/16092531174040634720noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-53641437140546993262009-12-01T13:19:29.238+00:002009-12-01T13:19:29.238+00:00I would certainly reccomend exposing an IQueryable...I would certainly reccomend exposing an IQueryable interface! It's so versatile. This is surely the future of web services and APIs.Joehttps://www.blogger.com/profile/15912683048610862404noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-82121379422270030122009-03-02T10:02:00.000+00:002009-03-02T10:02:00.000+00:00David,I agree. One way of seeing this discussion i...David,<BR/><BR/>I agree. One way of seeing this discussion is as the collision of functional and object oriented program paradigms. Much of the attraction of functional programming, from which LINQ takes most of its ideas, is that execution can be defered and optimised at runtime. This doesn't always sit well with many of the hard won principles of object oriented design. On the one hand we don't want to insert artificial boundaries for optimisation by saying, for instance, that querying should not be specified outside of a repository, because that removes our tools ability to do optimisation. On the other hand, with our OO hats on, we want to cleanly separate our concerns.Mike Hadlowhttps://www.blogger.com/profile/16441901713967254504noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-56454846739074454632009-02-27T20:57:00.000+00:002009-02-27T20:57:00.000+00:00I was going to comment that a distinction needs to...I was going to comment that a distinction needs to be made between returning IQueryable from the data access layer and returning IQueryable from the business layer. But it seems that conclusion was eventually reached without my help :)<BR/><BR/>I think it also needs to be pointed out that IQueryable != direct data access; at least, not necessarily. All IQueryable does is take expression trees instead of functions in its implementation of the standard query operators. This allows the query to be processed in whatever way you like. So, for example, you could write an IQueryable implementation which enforces that any query on an entity set has a predicate which results in the query being indexable (see the Facebook API at http://wiki.developers.facebook.com/index.php/FQL#The_Query_Language for an example of this approach, although it is obviously not .NET or LINQ based). This can be a very powerful mechanism for "having your cake and eating it too."David Nelsonhttps://www.blogger.com/profile/02439589027804976861noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-29902090938790189782009-02-20T03:41:00.000+00:002009-02-20T03:41:00.000+00:00Interesting way to think about it. I'm currently ...Interesting way to think about it. I'm currently playing with doing this myself.Benhttps://www.blogger.com/profile/12809201725269923209noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-74199813842523171342009-01-15T17:38:00.000+00:002009-01-15T17:38:00.000+00:00"A blog post with some concrete examples would be ..."A blog post with some concrete examples would be excellent. I'd very much like to see an actual implementation of the patterns you are talking about. Do you have an open source project that I could look at?"<BR/><BR/>I could definitely blog about it but I never open-sourced it, actually I couldn't open source it because it was for a company but it wouldn't be hard to do cause it's just a few classes talking to NHibenate but I just never got around to it.Colin Jackhttps://www.blogger.com/profile/01403166737046938219noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-81356721531503247022009-01-15T15:44:00.000+00:002009-01-15T15:44:00.000+00:00Thanks Mike, I am thinking of just using ADO.NET ...Thanks Mike, I am thinking of just using ADO.NET directly. Any advice with Data Access with .NET (using MONO) and MySQL would be appreciated. I read LINQ to Entities might work...not sure?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-48162761907992532632009-01-15T14:05:00.000+00:002009-01-15T14:05:00.000+00:00I agree the discussion around this post has been g...I agree the discussion around this post has been great.<BR/><BR/>I'll be at the OpenSource .Net Exchange at Skillsmatter so hopefully I'll get a chance to chat to you then.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-28234414251089709092009-01-15T13:20:00.000+00:002009-01-15T13:20:00.000+00:00Colin, Thanks for pointing to that that discussion...Colin, Thanks for pointing to that that discussion on the DDD list, it was very useful.<BR/><BR/>A blog post with some concrete examples would be excellent. I'd very much like to see an actual implementation of the patterns you are talking about. Do you have an open source project that I could look at?<BR/><BR/>I also like your point about segregating the interface to pull out IDelete, IPersist. That could play very nicely in an DI scenario.<BR/><BR/>This debate has really moved my ideas about repository forward. I'm still not convinced that returning IQueryable'1 is a bad idea, but at least I'm far more aware of the arguments against it.Mike Hadlowhttps://www.blogger.com/profile/16441901713967254504noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-50217160789290743462009-01-15T12:58:00.000+00:002009-01-15T12:58:00.000+00:00"I think part of the disagreements were having are..."I think part of the disagreements were having are about semantics. I'm starting to agree with the DDD guys that 'repository' is probably the wrong name a generic persistence store. How about IPersist'1 instead? If we accept that 'repository' is an invention of DDD gurus like Fowler and Evans, then we should try to keep to its original meaning."<BR/><BR/>Yeah I like that way of looking at it, what we're moving back to is a domain focussed DAO approach (with slightly different constraints). <BR/><BR/>It might have advantages in some situations but I'm not sure I view it as a repository.<BR/><BR/><BR/><BR/>"I'm now thinking I would keep IRepository'1/IPersist'1 but use it internally in a repository, especially in larger applications. Do we return IQueryable'1 from these repositories? Well the paging/sorting argument says we should."<BR/><BR/>So yeah thats what I've been doing for a couple of years, basically. I have concrete domain focussed repositories like ICustomerRepository that have specific methods like GetActiveAccounts (or that take specifications). They entirely encapsulate persistence and return aggregate routes or aggregated values.<BR/><BR/>Internally these repositories used one or more repository helper classes, which did most of the heavy lifting.<BR/><BR/>This was pre-linq and paging/sorting was handled by the caller creating an object (kind like a specification) that specified how many results to go on and how to sort them and so on.<BR/><BR/>The solution was very DRY and, I think, worked well. Never blogged about it though, not sure why. I did mention it on DDD forum recently though (http://tech.groups.yahoo.com/group/domaindrivendesign/message/9053) and Tobin did a good job of writing it up.Colin Jackhttps://www.blogger.com/profile/01403166737046938219noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-6431058186742501622009-01-15T12:45:00.000+00:002009-01-15T12:45:00.000+00:00jnystrom, sorry I didn't reply to that particular ...jnystrom, sorry I didn't reply to that particular point before. You are right, you most definately should not return IQueryable'1 from your repository if your data access technology does not support LINQ. A different specification based pattern would probably be more appropriate. What are you using for data access?Mike Hadlowhttps://www.blogger.com/profile/16441901713967254504noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-30336011126603727392009-01-15T12:19:00.000+00:002009-01-15T12:19:00.000+00:00I completely agree that in a perfect world I would...I completely agree that in a perfect world I would use IQueryable, but if I am not using LINQ, how can I use IQueryable without loading a TON of data into memory when calling the repository methods?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-62099182432922513472009-01-15T09:40:00.000+00:002009-01-15T09:40:00.000+00:00jnystrom, Think Before Coding,The paging and filte...jnystrom, Think Before Coding,<BR/><BR/>The paging and filtering example is a good one. I think it's a great reason *for* returning IQueryable'1 from the repository. You can separate UI concerns from the domain model and still have efficient fetching from the DB. <BR/><BR/>But the it doesn't stop there. After embracing IQueryable'1 I've discovered one nice refactoring after another. <BR/><BR/>I think part of the disagreements were having are about semantics. I'm starting to agree with the DDD guys that 'repository' is probably the wrong name a generic persistence store. How about IPersist'1 instead? If we accept that 'repository' is an invention of DDD gurus like Fowler and Evans, then we should try to keep to its original meaning. <BR/><BR/>I'm now thinking I would keep IRepository'1/IPersist'1 but use it internally in a repository, especially in larger applications. Do we return IQueryable'1 from these repositories? Well the paging/sorting argument says we should.Mike Hadlowhttps://www.blogger.com/profile/16441901713967254504noreply@blogger.comtag:blogger.com,1999:blog-15136575.post-10661802926189676612009-01-14T17:38:00.000+00:002009-01-14T17:38:00.000+00:00For instance, if I only need uncomplete orders, I ...For instance, if I only need uncomplete orders, I dont want to get every order in the system first, then filter in code. Since I dont have LINQ, I dont have the delayed execution, I would need to get the data in the repo. Why not just let the db filter at that point, if I do NOT have the ability to use LINQ?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-29054222564527602612009-01-14T17:35:00.000+00:002009-01-14T17:35:00.000+00:00I understand in a perfect world we would not want ...I understand in a perfect world we would not want the filtering to happen in the repo, but with not being able to use LINQ I dont want to get all data and load it up in mem then filter. I want to do the filtering in the db at that point, right?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-1657789648331388442009-01-14T16:46:00.000+00:002009-01-14T16:46:00.000+00:00I'm not fond of generic repositories. But it's sti...I'm not fond of generic repositories. But it's still possible to return IQueryable from properties or methods on a specific repository.<BR/><BR/>IMHO it's better to encapsulate the business selection logic in the repository methods, but enable code that use the result to order or page it.<BR/><BR/>Ordering and paging are presentation problems that usually polute domain model implementations. Linq IQueryable is a powerfull way to make it transparent AND enficient.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-73131721920416938202009-01-14T13:03:00.000+00:002009-01-14T13:03:00.000+00:00Any ideas when you cant use LINQ? I was wondering...Any ideas when you cant use LINQ? I was wondering about the Repositories and putting methods on them that would do filtering in the DB, ideas? Not sure if this breaks the repository pattern ideaAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-34153390197213046312009-01-14T09:27:00.000+00:002009-01-14T09:27:00.000+00:00@MikeForget the All(Predicate`1 pred), but my poin...@Mike<BR/><BR/>Forget the All(Predicate`1 pred), but my point remains as noted by Michael.<BR/><BR/>Re AsEnumerable():<BR/>I'm just saying that there are maybe situations it feels it is necessary to do AsEnumerable() because you have to check calculated properties and this won't work as translated into a Linq<BR/>database. A specialized Query object can deal with that an issue optimized database queries.<BR/><BR/>I will have to agree though that Linq is great and it would be fantastic if we didn't have to deal with blooming relational databases.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-15136575.post-14371097454881840812009-01-14T09:15:00.000+00:002009-01-14T09:15:00.000+00:00Daniel,If you're going to return IQueryable'1 ther...Daniel,<BR/><BR/>If you're going to return IQueryable'1 there's no need to pass a predicate to your 'All' method. IQueryable gives you access to the expression.<BR/><BR/>Why does allowing IQueryable'1 to be returned make code duplication any more likely? I can just as easily write duplicate logic inside a specification. It's the "new devs won't understand it" argument again.<BR/><BR/>Writing .AsEnumerable() is the same as passing a specification that just says 'get everything'. How is that safer?Mike Hadlowhttps://www.blogger.com/profile/16441901713967254504noreply@blogger.com