I don’t like using the query syntax when it comes to Linq to AnythingButTheKitchenSink . Not sure why. Mostly, I guess, is that I seem to have a liking for Funcs and Actions to the point of stupidity and although you can work them into the query syntax, it just doesn’t look right.
Now with most of the Linq methods like Where or First, it’s simple once you understand lamdba expressions:
.SomeMethod(someField => someField.Property == value);
Now what about join?
So inner selector with an outer selector and a selector selects a selecting selector. Right got it.
Well let’s try to break it down. First part is
this IEnumerable<TOuter>
So being that this is an extension method meaning this is the collection you are using this method on.
IEnumerable<TInner> inner
So second field must be the list you want to join to. Ok so far.
Func<TOuter, TKey> outerKeySelector
Now this is where it gets a little odd looking. We know we have Outer and Inner lists so there needs to be a way to join on something. Say Outer is User and Inner is UserAddress. Most likely you will have a UserID on both lists. If not, you do now. So basically what this part of the method is saying is “Give me the stupid key on the Outer (User) list that I should care about.”
, user => user.UserID,
Next part:
Func<TInner, TKey> innerKeySelector
Pretty much the same thing, except now it needs the key from the Innerlist (UserAddress):
, address => address.UserID,
Now for the fun part:
Func<TOuter, TInner, TResult> resultSelector
Sa…say what? Ok this may look weird at first but you’ll hate yourself for not seeing it. It’s just asking you what to select from the two lists as some kind of hybrid object. See, you have to remember that with these linq methods, each method will produce a list. You can’t just chain them together and have it remember every list you’ve made:
user.Where(user => user.UserID > 1) // gives me a list of users
.Select(user => new { user.UserName, user.UserAddress, user.UserID }
//Gives me new items with user name, address, and user id
From this simple method chain, the end list is NOT the same as the one you started with or the one produced by the where method.
The last part of the Join method needs you to tell it what it’s going to produce from this join. Now it probably could just guess and include both lists, but that could be seen as sloppy and ultimately this gives you the choice of what exactly needs to be taken after the join. So:
, (user, address) => new { user, address});
So in this case, the newly created and joined list with be a list of items that have a user and address attached to it much like if you had a list of:
class UserAddressHybrid()
{
public User user { get; set; }
public UserAddress userAddress { get; set; }
}
So in other words, WHAT DO YOU WANT YOUR RESULTS TO LOOK LIKE?
In full it would look something like:
user.Join(address => address.User.UserID, //IEnumerable<TInner> inner
user => user.UserID, //Func<TOuter, TKey> outerKeySelector
address => address.UserID, //Func<TInner, TKey> innerKeySelector
(user, address) => new { user, address}); //Func<TOuter, TInner, TResult> resultSelector
Not so hard anymore, is it? You can start kicking yourself now.