views:

385

answers:

4

What do you think? should your DAO return an IQueryable to use it in your controllers?

Thanks in advance

+4  A: 

No. Your controllers shouldn't be handling any complex logic at all. Keep them slim; the Model (not DAO) should hand the Controller back everything it needs to pass onto the View.

Seeing queries (or even queryables) in a Controller class is something I would consider to be a code smell.

Aaronaught
Thanks Aaron, Nice point.Nonetheless there are some advantages of using IQueryable, like flexibility and maintainability of services - because the DAO interface would grow less using pipes and filters pattern.what do you think?
SDReyes
Spaghetti code always *seems* easier to maintain when a project is young and small; as the project grows, however, you'll start to regret not having a clear separation of concerns.
Aaronaught
Passing your IQueryable<T> and/or using pipes and filters does not equal spaghetti code. Spaghetti code is usually agnostic in regards to language or technique.
jfar
@jfar: The responsibility of a controller class is to serve up a view based on a user request. How is kicking around an `IQueryable` or having any awareness of the database at all anything but spaghetti code? It completely breaks the encapsulation provided by the domain model. (You *do have* a domain model, right?)
Aaronaught
No, I have a persistence model. Your standard for spaghetti code is very low if the difference between the two action method snippets in my answer is that one is "clean" and the other is spaghetti.
jfar
@jfar: A *persistence* model is not a *domain* model. My response to your example was that neither are spaghetti code *now*, but both are very likely to *become* spaghetti code as requirements change, because you are passing around a data (not domain) object in an area that should be persistence-ignorant.
Aaronaught
@Aaronaught. Spaghetti code is the result of spaghetti programmers. What your saying here is somehow domain modeling prevents bad programmers from creating bad code. Sure, a rich domain model helps lead people in the right direction but I think your trying to correlate linq in controllers to bad programmers which isn't the case.
jfar
@jfar: No, that is not what I am saying. Don't put words in my mouth. Spaghetti code results from too many patches applied to a short-sighted design regardless of programmer ability. Should we make all class fields public as well, for the sake of convenience? Perhaps give Views direct access to the `SqlConnection` so they don't have to go through that pesky Model/Controller layer when the client wants us to Just Get It Done?
Aaronaught
@Aaronaught. I didn't. I'm refuting your statements that LINQ in controllers programming practice makes spaghetti code and somehow domain driven designs makes everybody code rainbows. I'll again refer to my examples which have very small syntactic differences and do not expose underlying persistence information at all. The only difference is LINQ has more dots. ;) I'm going to stop now. Your starting to throw Straw Men around. Yes, of course I want to put SQL code in my javascript. :/ And your picking bad examples too; there are no private fields in Python .
jfar
+2  A: 

At the moment, it sounds attractive, but really isn't.

Mark Seemann
Thanks : )Nice thread
SDReyes
+1  A: 

If you follow the "fat models, skinny controllers" paradigm then no.

See this post on the Fat Controller anti-pattern.

Robert
Thanks Robert. : )
SDReyes
+1  A: 

I love passing IQueryable into my controllers because I don't have to create lame paging and sorting methods in every single DAO method and interface throughout the lifetime of my apps development.

GetCustomersByLastname( string lastname )

Quickly Becomes

GetCustomersByLastname( string lastname, string sortcolumn, int pagesize, int page )

Again and again and again and again. Bleck!

With IQueryable you can take implement paging and sorting in orthogonal ways such as taking advantage of the IPagedList project. Returning IQueryable also give you easy access to total object .Count() without more perversion of your data layer.

@Robert s argument of IQueryable equals fat controllers is very shaky. A Fat controller would be similar to the bloated .aspx.cs pages of yore. If all your doing is connecting to your DAL and then shipping the results off your don't gain "fatness" from your query technique, you gain it from shoving lots of logic inside inside a single class. You do not get a Fat Controller because of your data access methods unless you start rolling logging, notifications, and other orthogonal concerns inside.

public ActionResult Detail( string searchTerm )
{
    var model = MyDAL.MyObjects( searchTerm );
}

vs:

public ActionResult Detail( string searchTerm )
{
    var model = MyDAL.MyObjects.Where( x => x.Name == searchTerm );
}

I don't see a compelling difference.

@Mark Seemann 's answer is equally shaky. Sure, you may change your entire data layer in the middle of a project but that is going to be a complex disaster no matter how abstracted you are. The example he uses is switching from Linq2Sql to Windows Azure's table storage. RDBMS to Key/Value store? And the pain point is your Repository implementation? Going from RDBMS to a Key/Value store is going to be some craziness thats going to be horrible no matter what.

Mark also brings up Domain Driven Design in his argument. Is that the type of system your building. Is there enough "Domain" rather than pure CRUD scenarios that make this approach valuable? If not then why bother?

Using and LINQ and the IQueryable interface gives you less of the pain of switching data layers anyway. If your switching between ORMs that support LINQ and IQueryableProvider ( I think thats the name ) than only the downstream code cares about that change. Your controllers would stay the same switching between from most ORMs on the market now.

jfar
If the purpose of this is sorting and paging then this can easily be accomplished with an `IPager<T>` interface that wraps the `IQueryable<T>` but still outputs a domain model. I use this approach, it requires very little code, and eliminates any direct interaction between controller and database. Your comparison is strange; it seems as though your project is lacking a domain model, in which case, of course there is not much difference - but if you don't have a domain model then you don't really have have MVC.
Aaronaught
Also note that any paging done using `IQueryable<T>` (I'm guessing it's a combination of `OrderBy` and `Skip` here) is going to be inefficient. While it may be more convenient to play with that, efficient paging usually requires the use of raw SQL or stored procedures, neither of which lend themselves well to `IQueryable<T>` filters/projections.
Aaronaught
@Aaronaught I'm referring to domain driven design. Domain model to me means somebody is using a DDD technique to build their app. I was pointing out that Roberts linked answer advocates doing something for the sake of DDD. If your not using DDD than some of the reasons for returning IEnumerable<T> just aren't there.
jfar
@Aaronaught Your second comment is false. All Linq enabled.Net ORMS generate the efficient paging sql just fine using .Skip() and .Take() off of an IQueryable<T>.
jfar
I am not and never have been an advocate of DDD, MDD, TDD, or whatever the latest Agile flavour of the month is; however, I do believe that a domain model independent of the relational model is simply good design in almost any application. The few times that I have skipped that stage in the past and exposed data objects through high-level abstractions, I have subsequently regretted the decision. Just my two cents. :)
Aaronaught
`Skip` and `Take` generate a paging query using `ROW_NUMBER`. `ROW_NUMBER` is a great and versatile tool but really is not efficient for large tables. One example of a much more performant solution is here: http://www.4guysfromrolla.com/webtech/042606-1.shtml. There's over an order-of-magnitude difference over `ROW_NUMBER`.
Aaronaught
@Aaronaught. I think your assuming I return SqlDataReader's into my controllers or something equally hideous. 90% of the time the mapping done by ORMs creates a good enough domain model for shipping around CRUD screens around. I think we are just coming from different contexts here. I have never regretted not making a full on domain model outside from ORM mapping and I find it keeps things simpler and faster to code.
jfar
@jfar: `SqlDataReader` doesn't implement `IQueryable` as far as I know. I understood fully what you were talking about; a collection of Linq to SQL classes constitutes an anemic domain model at best. Perhaps they work for you now, but as Mark explained, it will frequently bite you later. Paging is actually a perfect example of this; if you rely on `IQueryable<T>` for "convenient" paging, how would you change your paging implementation to something more efficient later on?
Aaronaught
@Aaronaught Your paging concern is very YAGNI. The article was a good read and I learned a lot but the perf stats weren't impressive. Nobody is going to page through a 100k record table. The users will be searching for something. This is a variation of the "what if you have gazillion users and gigabytes of data" argument, something most systems just won't have. I respect your opinion but it seems your coding against presumptions that your domain will be rich and your scale will be large. Stackoverflow was built with an "anemic model" and it seems to be working ok. ;)
jfar
@jfar: I can't believe you actually posted the acronym. That seems to be the default excuse now for just about any design flaw. Here are three more Agile TLAs for you: SRP, DIP, and SDP. SO has huge scale but very limited scope; most apps (especially business apps) *do* have rich domain models, and eventually need to scale up as well. I know this from experience. If you don't think a 1000% performance improvement is "impressive" then I hope I don't end up having to use one of your apps.
Aaronaught
@Aaronaught I think you stopped reading my last comment at Yagni. Swapping paging algorithms is probably the least of my concerns when developing. Nobody wants to page through 100k record tables; search functionality is required. Any citation for that 1000% improvement claim? That article you posed before certainly isn't a 1000% speed improvement. 4 year old hardware returning at 780 ms at its slowest is pretty good for a business app. Whats that a modern db server? 500 ms? And no 1000% performance over 780 ms for a business app isn't impressive, its a waste of time.
jfar