views:

408

answers:

4

Yesterday I posted this question regarding using lambdas inside of a Join() method to check if 2 conditions exist across 2 entities. I received an answer on the question, which worked perfectly. I thought after reading the MSDN article on the Enumerable.Join() method, I'd understand exactly what was happening, but I don't. Could someone help me understand what's going on in the below code (the Join() method specifically)? Thanks in advance.

if (db.TableA.Where( a => a.UserID == currentUser )
      .Join( db.TableB.Where( b => b.MyField == someValue ),
             o => o.someFieldID,
             i => i.someFieldID,
             (o,i) => o )
      .Any()) 
{
    //...
}

Edit: Specifically, I'm curious about the last 3 parameters, and what's actually going on. How do they result in the signature requirements of Func(TOuter, TKey), Func(TInner, TKey) etc.

+2  A: 

This query is saying join TableA to TableB where TableA.someFieldID == TableB.someFieldID and selecting the results from TableA and seeing if there are any results at all

In terms of SQL think of it like this, even if it's not Linq-to-SQL...if you are familiar with SQL maybe this makes more sense:

Select Count(*)
From TableA a
     Join TableB b
       On a.someFieldID = b.someFieldID

Then checking if Count(*) is > 0

Nick Craver
+9  A: 

The join syntax is

FirstTable.Join(SecondTable, FirstTableKeyExtractor, SecondTableKeyExtractor, Selector)

So you have two tables. You have some key that is common to both tables. You provide two key extractors that know how to get the key out of each row in the table.

The join logic identifies pairs of rows, one from each table, that have the same key.

Each of those rows is then run through the selector to project the result.

Does that answer your question?

Eric Lippert
Almost. I understand the first parameter being an entity (of a table matching those conditions), but I'm curious how the other 3 parameters work. What exactly is happening at this point:o => o.SomeFieldID,i => i.SomeFieldID,(o, i) => o
lush
@lush - The second parameter says which field on the original table you're matching on (`TableA.someFieldID`), the third parameter says which field on the joined table you're matching it to (`TableB.someFieldID`). The last parameter says from this newly joined set of tables, what you are selecting...in this case that's everything from `TableA`. In your code, you could replace `o` with `a` and `i` with `b`, that may make it a lot clearer to see.
Nick Craver
But how does the code "o => o.SomeFieldID" translate to "TableA.SomeFieldID"? What's going on under the hood?
lush
I've updated my answer to cover the o => o.SomeFieldId syntax.
Richard Hein
+1  A: 

Explanation of the Join.

b = object type of first table o = object type of first table i = object type of second table

  1. db.TableB.Where( b => b.MyField == someValue ) This is the element type of the second table
  2. o => o.someFieldID The key of the first table
  3. i => i.someFieldID The key of the second table (which will match the key in the first table)
  4. (o,i) => o The object to return, in this case the object type of the first table.
Mikael Svenson
+2  A: 

Eric and Nick have both provided good answers.

You may also write the Linq query expression using query syntax (vs. method syntax, which you are using in your example):

var query = from a in db.TableA 
            join b in db.TableB on a.someFieldID equals b.someFieldID
            where a.UserID == currentUser && b.MyField == someValue
            select a;

        if (query.Any()) {
            ...
        }

Update:

You seem to be stuck on the lambda expressions. It's a function that you pass around like a variable. A lambda expression is equivalent to an anonymous delegate (or anonymous method, to me more general).

Here is your query with the lambda expressions as delegates (replace EntityType with the type of your entity returned from TableA, of course):

if (db.TableA.Where( delegate(EntityType a) { return a.UserID == currentUser; } ) 
  .Join( db.TableB.Where( delegate(EntityType b) { return b.MyField == someValue; } ), 
         delegate(EntityType o) { return o.somefieldId); },
         delegate(EntityType i) { return i.someFieldId); },
         delegate(EntityType o, EntityType i) { return o; }) 
  .Any())  

{ //... }

NOTE: A lambda expression has important aspects that make it more than just an equivalent for anonymous methods. I recommend that you look through other SO questions and read online about lambda expressions in particular. They allow for very powerful ideas to be expressed in a much simpler and elegant way. It's a deep topic, but the basics are easy enough to understand. It's a function that you can pass around like a variable, or as a parameter to other functions.

Richard Hein
Yeah, I actually had it written this way previously, then after realizing that it could be accomplished using the Join method using lambdas, I figured I'd challenge myself. I still believe this way to be clearer, and will probably implement the solution this way, but I want to full understand the other technique first.
lush
In your updated example, shouldn't the second delegate(EntityType o) be delegate(EntityType i)?
Metro Smurf
Yes, thanks Metro Smurf (I can't believe those two words appear one after another lol).
Richard Hein
Perfect, this is exactly what I was looking for, given my understanding of implicit types and what little I know of anonymous methods and lambda expressions thus far. I especially appreciate your code example. Thank you.
lush