LINQ allows you do define simple functions/methods in-line with the query you are doing. These are called lambda expressions.
They have a special format to allow for in-line expressions.
Take a look at:
c => c + 1
Basically this is:
for each value c give me c + 1
Rather then complicating things with full-blown LINQ queries lets cheat and just use simple arrays.
For example:
int[] myInts = { 1, 9, 2, 8, 3, 7, 4, 6, 5 };
var largeInts = myInts.Where(x => x > 5);
The where clause requires a lambda expression that returns a boolean. Here we’re saying:
for each value x give me values where x > 5
Um, Brian, I have graduated college, this is kindergarten stuff.
Fine, lets go a bit more complicated:
string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
var shortDigits = digits.Where((digit, index) => digit.Length > index);
That’s two, yes, two variables here. If you haven’t realized by now LINQ is really syntactic sugar. When using arrays with LINQ, since all it is really going to do is just iterate over the values for you, you have access to an index variable that represents the index of where the query is.
Here it is saying
for each digit and its corresponding index give me the values where the length of the digit string is less then the index
meaning shortDigits ends up having the values:
{ “five”, “six”, “seven”, “eight”, “nine” }
Now, before we go too far I need to take a step back and clarify something from the last post. Remember I gave a basic LINQ statement as:
var sortedFiles = from file in files orderby file.LastWriteTime descending select file;
and then went on to say:
from file in files
basically for each file in our files list
orderby file.LastWriteTime
LastWriteTime is a property on FileInfo. files is a list of FileInfo, therefore each file is a FileInfo object. Intelli-sense is fully supported here so when you type in file and put the dot is shows you all the properties on file as if it were a standardly declared FileInfo.
descending
Like a standard DESC to an orderby clause. Like sql it implicitly orders by ASC so if you want descending you have to add it.
select file
return the file that applies to the orderby clause. This may seem a bit redundant and I would agree but it is still needed.
Okay, now that you have read this for a second time I really want to address that last section. It is only redundant when you want the full object available in the list. If all I wanted was a list of file names I would do:
var sortedFiles = from file in files orderby file.LastWriteTime descending select file.Name;
Yes, you can return a single property from the object.
BUT WAIT, THERE’S MORE!
Yes, Ron Popeil here with the amazing Create-The-Object-On-The-Fly ability of LINQ and the 3.5 framework.
That’s right, Set It and Forget it! and it will generate types ON THE FLY!!!
Say I not only want the file name but also want the time when the file was created (CreationTime). Well, just add that to your LINQ and now you have an IEnumerable with two properties:
var sortedFiles = from file in files orderby file.LastWriteTime descending select new { file.Name, file.CreationTime };
Yes, here you can see the real power of the var. sortedFiles is now a list consisting of some object (really a System.Linq.Enumerable.SelectIterator> object) that has the properties Name and CreationTime. This ability is called Anonymous Types.
Taking this idea one step further, remember that a LINQ query is really just SQL you can put in your code to enumerate over lists.
That’s it Brian, your f-in with me!
Yes, yes I am.
Wow, deja vu.
As you have seen the ability to create anonymous types on the fly as well as using lambda expressions aren’t really just simply enumerating over a list. But back to the sql idea. Let’s do a join in a LINQ query.
Say we have a Product class that looks like:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
}
and an Inventory class that looks like:
public class Inventory
{
public int InventoryId { get; set; }
public int ProductId { get; set; }
public int NumberOnHand { get; set; }
}
now take a look at this:
List<Inventory> InventoryList = new List<Inventory>();
InventoryList.Add(new Inventory { ProductId = 1, NumberOnHand = 3 });
InventoryList.Add(new Inventory { ProductId = 2, NumberOnHand = 0 });
List<Product> ProductList = new List<Product>();
ProductList.Add(new Product { ProductId = 1, Name = "Apple" });
ProductList.Add(new Product { ProductId = 2, Name = "Orange" });
var ProductsInInventory = from InventoryItem in InventoryList
join ProductItem in ProductList on InventoryItem.ProductId equals ProductItem.ProductId
where InventoryItem.NumberOnHand > 0
select new { ProductItem.Name, InventoryItem.NumberOnHand };
But Brian, that’s not how it’s done in SQL.
Well, really inner voice? Like I’ve never done SQL before.
Wasn’t the medication supposed to stop you from hearing me? ‘Cause it doesn’t seem to be doing a great job.
Oh that? Stopped takin’ it. Figured without my inner voice I was just talking to myself.
But yes, Inner Voice is correct, that is not how it’s done. But I did say that LINQ was like sql, not exactly sql.
The way a join is done is a bit different then in SQL when doing an implicit inner join but it is still pretty simple and actually very similar to doing an explicit inner join. We just get so used to doing implicit joins we forget about explicit joins. Once again, things are done in LINQ to facilitate the compiler optimizations and Intelli-sense.
Well, now your foot is wet and I’ve taken you a bit further into the world of LINQ. Next on the topic is more on lambda expressions and other types of joins.
Later ‘yall,
Brian (and his inner voice)