Joining composite keys with LINQ

Joining composite keys with LINQ

LINQ makes working with data in its various guises easier. By intergating it into the language, we have rich integrated support for working with data. However, there are times where the syntax is slighly different from what you would typically expect with TSQL. Once case where this occurs is when trying to join two data sources that are related by more than one field (also know as a composite key). This differs from standard joins where one table has a primary key and the other table has a foreign key id. Here's a sample table structure for a standard join in Northwind between the Products and Categories:

With Linq, this join could be represented with the following query:

Dim query = _
     From p In dc.Products _
     Join c In dc.Categories _
     On p.CategoryID Equals c.CategoryID _
     Select p.ProductName, c.CategoryName

So far, there's not much new in this query. Here, "p" is the outer variable and "c" is the inner variable of the join. Likewise, p.CategoryID is the outerKeySelector and c.CategoryID is the InnerKeySelector in the Join extension method.

This works fine when we have single values that can be compared easily. However, how can we specify multiple fields for the KeySelectors on the join? In my work with reverse mortgages, we have a situation where the loan amount is based in part on the lending limits set forth by HUD. In their limits, they specify the State and County for each limit. In this case, I need to join those values against the loan property's state and county to come up with the limit amount. Let's consider the following partial table schemas.

In this case, we could join the tables in TSQL with the following query:

SELECT LendingLimits.Amount
FROM LendingLimits
INNER JOIN Property
ON LendingLimits.State=Property.State AND
      LendingLimits.County=Property.County
WHERE PropertyId=@SearchValue  AND
      EffectiveDate = @TargetDate

Unfortunately, the Join extension method does not support the ability to provide the InnerKeySelector/OuterKeySelector as a series of expressions. However, when dealing with objects, we can compare objects to see if they equal each other. Therefore, the solution in this case is to join two anonymous types and compare them against each other. Here's the corresponding LINQ query. Notice the difference in the On clause. If you understand working with objects, this syntax should make sense.

Dim query1 = _
    From l In dc.LendingLimits _
    Join p In dc.Properties _
    On New With {l.County, l.State} Equals _
          New With {p.County, p.State} _
    Where p.PropertyId = SearchValue And _
          l.EffectiveDate = TargetDate _
    Select l.Amount

Posted on 3/18/2008 12:48:00 AM - Comments(6)
Categories: VB Dev Center LINQ VB
Comments:
  • Gravatar What is the C# equivalent?
    Rob (Posted on 4/13/2008 5:08:00 PM)
  • Gravatar C# equivalent is



    ..join... on new {a.x, a.y} equals new {b.x, b.y}...
    Martin K (Posted on 5/13/2008 7:16:00 AM)
  • Gravatar Hi,

    How can i perform non equal join...?
    Jay (Posted on 8/21/2009 1:39:00 AM)
  • Gravatar Jay, In cases where you need more complex joins, you can use the ANSI 82 syntax where the join is in the Where clause.
    Jim (Posted on 8/21/2009 11:22:00 AM)
  • Gravatar What if I we have more than two tables.I have checked so many sites,searching for this issue and I found no good solution.

    does anybody know how to join more than two table with linq with VB.net
    Akbar Ghasemi (Posted on 10/3/2009 4:35:00 PM)
  • Gravatar Akbar, if you have multiple tables, you would handle them much the same way as you do two tables. If you are joining them using the LINQ Join clause, just add another From ... Join clause and you should be set. If you are navigating through a object heirarchy, you can navigate through them directly if there are 1-1 releationships. If you are navigating through 1-n relationships, you may need to add more from clauses like the following:



    Dim query = From c in dc.Customers _

    From o in c.Orders _

    From od in o.OrderDetails _

    Select c.CustomerName, od.Quantity, od.Price



    Note here, the join details are kept in the association mapping so we don't need to state them in the query. If you don't have natural associations between the entities, you can do the join as follows:



    Dim query = From c in dc.Customers _

    Join o in dc.Orders On c.CustomerId Equals o.CustomerId _

    Join od in dc.OrderDetails On o.OrderId Equals od.OrderId _

    Select c.CustomerName, od.Quantity, od.Price
    Jim Wooley (Posted on 10/4/2009 2:56:00 PM)