567

I'm having trouble with a query written in LINQ and Lambda. So far, I'm getting a lot of errors here's my code:

int id = 1;
var query = database.Posts.Join(
    database.Post_Metas,
    post => database.Posts.Where(x => x.ID == id),
    meta => database.Post_Metas.Where(x => x.Post_ID == id),
    (post, meta) => new { Post = post, Meta = meta }
);

I'm not sure if this query is correct.

4
  • 14
    what are you trying to accomplish? Commented May 4, 2010 at 17:55
  • 4
    what do you want the query to do in a sentence?
    – hunter
    Commented May 4, 2010 at 17:55
  • 6
    Your key selectors are way too complicated. If you want to select by id, just x=>x.ID is fine. Commented May 4, 2010 at 18:00
  • 1
    I wanted to get a post from the database and the meta data for that post.
    – David
    Commented May 4, 2010 at 18:21

10 Answers 10

1299

I find that if you're familiar with SQL syntax, using the LINQ query syntax is much clearer, more natural, and makes it easier to spot errors:

var id = 1;
var query =
   from post in database.Posts
   join meta in database.Post_Metas on post.ID equals meta.Post_ID
   where post.ID == id
   select new { Post = post, Meta = meta };

If you're really stuck on using lambdas though, your syntax is quite a bit off. Here's the same query, using the LINQ extension methods:

var id = 1;
var query = database.Posts    // your starting point - table in the "from" statement
   .Join(database.Post_Metas, // the source table of the inner join
      post => post.ID,        // Select the primary key (the first part of the "on" clause in an sql "join" statement)
      meta => meta.Post_ID,   // Select the foreign key (the second part of the "on" clause)
      (post, meta) => new { Post = post, Meta = meta }) // selection
   .Where(postAndMeta => postAndMeta.Post.ID == id);    // where statement
5
  • 12
    @Emanuele Greco, regarding your edit, "Equality on ID fields is set in JOIN condition; you don't need to use WHERE clause!": the WHERE clause isn't testing equality between ID fields, it's testing equality between the post ID column and the id parameter declared outside the query. Commented May 27, 2013 at 15:45
  • 1
    Sometimes explanations of lambda are written in lambda. Explained well.
    – Pinch
    Commented Jun 28, 2019 at 17:52
  • i keep trying to use .includes but it only gets me so far. The "from post in database.Posts" method is something i have to remember. I also have a DapperDb injected into my controllers for complex queries but for simple joins ill use the from post in database.Posts example. Its really convenient. If i suffer any performance penalties then ill convert the query to Dapper
    – Andy
    Commented Jun 27, 2020 at 10:59
  • Won't meta.Post_ID be the inner key that limits the number of joined rows, and wouldn't this mean that if the goal is to look up a certain post with metadata if present, that even if the post is in the Posts table, if it has no metadata it will not be part of the result set? Commented May 10, 2022 at 13:45
  • @HenrikErlandsson that's correct, this example would generate SQL for an inner join. It's also possible to do a left outer join, see: learn.microsoft.com/en-us/dotnet/csharp/linq/… Commented May 10, 2022 at 16:21
98

You could go two ways with this. Using LINQPad (invaluable if you're new to LINQ) and a dummy database, I built the following queries:

Posts.Join(
    Post_metas,
    post => post.Post_id,
    meta => meta.Post_id,
    (post, meta) => new { Post = post, Meta = meta }
)

or

from p in Posts
join pm in Post_metas on p.Post_id equals pm.Post_id
select new { Post = p, Meta = pm }

In this particular case, I think the LINQ syntax is cleaner (I change between the two depending upon which is easiest to read).

The thing I'd like to point out though is that if you have appropriate foreign keys in your database, (between post and post_meta) then you probably don't need an explicit join unless you're trying to load a large number of records. Your example seems to indicate that you are trying to load a single post and its metadata. Assuming that there are many post_meta records for each post, then you could do the following:

var post = Posts.Single(p => p.ID == 1);
var metas = post.Post_metas.ToList();

If you want to avoid the n+1 problem, then you can explicitly tell LINQ to SQL to load all of the related items in one go (although this may be an advanced topic for when you're more familiar with L2S). The example below says "when you load a Post, also load all of its records associated with it via the foreign key represented by the 'Post_metas' property":

var dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Post>(p => p.Post_metas);

var dataContext = new MyDataContext();
dataContext.LoadOptions = dataLoadOptions;

var post = Posts.Single(p => p.ID == 1); // Post_metas loaded automagically

It is possible to make many LoadWith calls on a single set of DataLoadOptions for the same type, or many different types. If you do this lots though, you might just want to consider caching.

1
  • 1
    LinqPad and CRM 2016 ?
    – Kiquenet
    Commented Dec 7, 2016 at 14:40
77

Daniel has a good explanation of the syntax relationships, but I put this document together for my team in order to make it a little simpler for them to understand. Hope this helps someoneenter image description here

6
  • That won't work when you are simply dealing with a list of values like we are here. There is no id property on the object. Commented Mar 13, 2018 at 15:38
  • I did find this really useful, but I got an error that required me to add the joining column. Also looking at the answer posted by @Mark Byers, the joining column has the Post_ID field in the second alias meta => meta.Post_ID. In the example in this illustration, the g.id part of the original select statement JOIN gStatus g on g.id is not replicated in the final Lambda expression. Commented Mar 13, 2018 at 18:03
  • 3
    I wasn't trying to post this as a reference to the actual linq required to answer posted by the OP, it was more of a reference for how to move SQL to a Linq format so my inputs were a little different than the original question. If I had created a class for the gStatus values I would have put an id property on it and then yes it would have joined with g => g.id I used a list of values to try to keep the code as simple as possible. Commented Mar 14, 2018 at 14:07
  • @Talspaugh27 So why in the SQL query does it join to gStatus on g.id? Is that a mistake or intentional?
    – Drammy
    Commented Nov 12, 2019 at 12:49
  • @Drammy in a sql table each column has to have a name, so since this was a 1 column table strictly for holding these ids, I just used a column named id, the List<int> doesn't have that issue. If I had set it up as such public class IdHolder{ int id } then used that object in the gStatus List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); then it would have changed the Linq to be t =>t.value.TaskStatusId, g=>g.id does that change make sense? Commented Nov 18, 2019 at 18:19
41

Your key selectors are incorrect. They should take an object of the type of the table in question and return the key to use in the join. I think you mean this:

var query = database.Posts.Join(database.Post_Metas,
                                post => post.ID,
                                meta => meta.Post_ID,
                                (post, meta) => new { Post = post, Meta = meta });

You can apply the where clause afterwards, not as part of the key selector.

9

Posting because when I started LINQ + EntityFramework, I stared at these examples for a day.

If you are using EntityFramework, and you have a navigation property named Meta on your Post model object set up, this is dirt easy. If you're using entity and don't have that navigation property, what are you waiting for?

database
  .Posts
  .Where(post => post.ID == id)
  .Select(post => new { post, post.Meta });

If you're doing code first, you'd set up the property thusly:

class Post {
  [Key]
  public int ID {get; set}
  public int MetaID { get; set; }
  public virtual Meta Meta {get; set;}
}
8

I've done something like this;

var certificationClass = _db.INDIVIDUALLICENSEs
    .Join(_db.INDLICENSECLAsses,
        IL => IL.LICENSE_CLASS,
        ILC => ILC.NAME,
        (IL, ILC) => new { INDIVIDUALLICENSE = IL, INDLICENSECLAsse = ILC })
    .Where(o => 
        o.INDIVIDUALLICENSE.GLOBALENTITYID == "ABC" &&
        o.INDIVIDUALLICENSE.LICENSE_TYPE == "ABC")
    .Select(t => new
        {
            value = t.PSP_INDLICENSECLAsse.ID,
            name = t.PSP_INDIVIDUALLICENSE.LICENSE_CLASS,                
        })
    .OrderBy(x => x.name);
8

Query Syntax for LINQ Join

var productOrderQuery = from product in Product.Setup()//outer sequence
                        join order in OrderDetails.Setup()//inner sequence
                        on product.Id equals order.ProductId //key selector
                        select new//result selector
                        {
                            OrderId = order.Id,
                            ProductId = product.Id,
                            PurchaseDate = order.PurchaseDate,
                            ProductName = product.Name,
                            ProductPrice = product.Price
                        };

Method Syntax for LINQ Join

var productOrderMethod = Product.Setup().//outer sequence
    Join(OrderDetails.Setup(), //inner sequence
    product => product.Id//key selector
    ,order=> order.ProductId //key selector
    ,(product,order)=> //projection result
        new
        {
            OrderId = order.Id,
            ProductId = product.Id,
            PurchaseDate = order.PurchaseDate,
            ProductName = product.Name,
            ProductPrice = product.Price
        }
    );

Product.cs for reference

class Product
{
    public int Id { get; set; }
    public string Name { get; set; }

    public decimal Price { get; set; }
    public static IEnumerable<Product> Setup()
    {
        return new List<Product>()
        {
            new Product(){Id=1, Name="Bike", Price=30.33M },
            new Product(){Id=2, Name="Car", Price=50.33M },
            new Product(){Id=3, Name="Bus", Price=60.33M }
        };
    }
}

OrderDetails.cs class for reference

class OrderDetails
{
    public int Id { get; set; }
    public virtual int ProductId { get; set; }

    public DateTime PurchaseDate { get; set; }
    public static IEnumerable<OrderDetails> Setup()
    {
        return new List<OrderDetails>()
        {
            new OrderDetails(){Id=1, ProductId=1, PurchaseDate= DateTime.Now },
            new OrderDetails(){Id=2, ProductId=1, PurchaseDate=DateTime.Now.AddDays(-1) },
            new OrderDetails(){Id=3, ProductId=2, PurchaseDate=DateTime.Now.AddDays(-2) }
        };
    }

}
1
  • Can you provide an answer with where condition in Method Syntax for LINQ Join
    – raw_hitt
    Commented Jan 19, 2022 at 18:51
7

It could be something like

var myvar = from a in context.MyEntity
            join b in context.MyEntity2 on a.key equals b.key
            select new { prop1 = a.prop1, prop2= b.prop1};
6

This linq query Should work for you. It will get all the posts that have post meta.

var query = database.Posts.Join(database.Post_Metas,
                                post => post.postId, // Primary Key
                                meta => meat.postId, // Foreign Key
                                (post, meta) => new { Post = post, Meta = meta });

Equivalent SQL Query

Select * FROM Posts P
INNER JOIN Post_Metas pm ON pm.postId=p.postId
2
  • 1
    you closed the parentheses where after third param..."no overload for Join takes three arguments" Commented Aug 23, 2018 at 16:38
  • 5
    This is identical to the accepted answer and 7 years later -1 Commented Mar 26, 2019 at 16:22
1

1 equals 1 two different table join

var query = from post in database.Posts
            join meta in database.Post_Metas on 1 equals 1
            where post.ID == id
            select new { Post = post, Meta = meta };

Not the answer you're looking for? Browse other questions tagged or ask your own question.