C# - Chaining LINQ Queries Together
One of the cool things I discovered while getting to grips with LINQ is that you can chain LINQ queries together. This helps when trying to follow the Don't Repeat Yourself (DRY) approach to design, which I'm sure we all do.
While using DataSets and SQL/Stored Procedures in ASP.NET projects I was finding myself repeating the same SQL over and over. Any change to the column in any of the tables would likely mean a change to all the queries I had stored. Ergh.
As an example, let's say you have your zoo and its animals. If you want a list of animals that are viewable by the visiting public you might have a query like this:
public IEnumerable<Animal> GetViewableAnimals() { return zoo.Animals.Where( a => a.Alive && !a.Sick && a.DepartureDate == null ).OrderBy(a => a.Name); }
Not a great real-world example, but you get the idea - there's a base logic for what set of animals any other list should ever derive from.
Now, let's say you want get animals of a certain types (monkeys?). You might write another method, like so:
public IEnumerable<Animal> GetAnimalsByType(AnimalType type) { return zoo.Animals.Where( a => a.Alive && !a.Sick && a.DepartureDate == null && a.Type = (int)type ).OrderBy(a => a.Name); }
But, wait. You've repeated yourself! What if the logic by which you decide if an animal is "viewable" changes (say, you let the public see sick animals)!? You need to update all methods.
Well, you could instead have each method return an IQueryable object and chain them together. Like so:
public IQueryable<Animal> GetAnimalsByType(AnimalType type) { return GetViewableAnimals() .Where(a => a.Type == (int)type) .OrderBy(a => a.DateOfBirth); } public IQueryable<Animal> GetViewableAnimals() { return zoo.Animals.Where( a => a.Alive && !a.Sick && a.DepartureDate == null ).OrderBy(a => a.Name); }
Notice how the "by type" method first gets the "base" query from the other method and then appends an extra clause to the "where" part of the statement.
Note that it's not until you try to access the data in the IQueryable class that it performs the SQL query. So, the above does not perform two SQL queries. Just one!
A fuller example would be the following example of a repository class:
public class AnimalRepository { ZooDataClasesContext zoo = new ZooDataClasesContext(); public IQueryable<Animal> Search(string query) { return GetViewableAnimals() .Where(a => a.Name.Contains(query)) .OrderBy(a => a.Name); } public IQueryable<Animal> GetAnimalsByType(Animal.Type type) { return GetViewableAnimals() .Where(a => a.type == (int)type) .OrderBy(a => a.DateOfBirth); } public IQueryable<Animal> GetAnimalsByName(string name) { return GetViewableAnimals() .Where(a => a.Name == name) .OrderBy(m => m.DateOfBirth); } public IQueryable<Animal> GetAnimalsByType(AnimalType type) { return GetViewableAnimals() .Where(a => a.Type == (int)type) .OrderBy(a => a.DateOfBirth); } public IQueryable<Animal> GetViewableAnimals() { return zoo.Animals.Where( a => a.Alive && !a.Sick && a.DepartureDate == null ).OrderBy(a => a.Name); } }
There might be other or better ways to do this, but, for now, it's my preferred approach. I came up with the above while working on an app that has a fairly complex set of rules about what the base set of viewable rows are for the current user. Chaining queries like this means I only have to write this logic once.
Sweeet. Jake - you've made the light go on for me with this! My mind is abuzz now and I'm thoroughly distracted from the Domino work I'm supposed to be doing. I'll have the client forward the tab to you for lost productivity. ;-)
Reply
I'm doing Domino ("classic" !) work this week. It's hard to stay focused when I know what else I *could* be doing.
But, yeah. Once the light lights up there's no end to what you can do. I'm constantly thinking of improvements to my working practice. I love it.
Reply
Some more reading if you're interested:
http://daniel.wertheim.se/2011/02/07/c-clean-up-your-linq-queries-and-lambda-expressions/
Saves updating your query criteria in multiple places if the business changes their mind about what constitutes a viewable animal etc. (maybe the zoo goes into taxidermy or something? :D).
Reply
Definitely interested. He's now in my RSS reader and on my twitter.
I have an insatiable thirst for all things C# at the moment.
Reply
Show the rest of this thread
Great example. I'm distracted just like Jerry :-)
Reply
If you're writing Linq queries you might want to check out LinqPad (http://www.linqpad.net). It's like SQL server management studio but lets you mess around with Linq (amongst others) instead.
Reply
It took me awhile to move away from ADO but I'm amazed how at how
linq continue to unfold, there are so many cool time savers poppin up. Your doing something that would take you 200 lines in ado in LINQ as an AT in one line F@ckin Ridiculous !
@CJ Linq Pad is Cool ! I don't use SSMS anymore lol.
Reply
Can I ask whether there is a way to use Linq to connect to Domino Web Source? thanks heps
Reply