556

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.

starball
  • 20,030
  • 7
  • 43
  • 238
David
  • 5,579
  • 3
  • 16
  • 4

10 Answers10

1273

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
Daniel Schaffer
  • 56,753
  • 31
  • 116
  • 165
  • 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. – Daniel Schaffer May 27 '13 at 15:45
  • 1
    Sometimes explanations of lambda are written in lambda. Explained well. – Pinch Jun 28 '19 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 Jun 27 '20 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? – Henrik Erlandsson May 10 '22 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: https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-left-outer-joins – Daniel Schaffer May 10 '22 at 16:21
96

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.

Damian Powell
  • 8,655
  • 7
  • 48
  • 58
72

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

Talspaugh27
  • 973
  • 9
  • 16
  • 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. – Talspaugh27 Mar 13 '18 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. – SausageFingers Mar 13 '18 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. – Talspaugh27 Mar 14 '18 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 Nov 12 '19 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 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 gStatus = new List(); 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? – Talspaugh27 Nov 18 '19 at 18:19
  • @Talspaugh27 Yeah sure, I guess I was just asking if the syntax in the image would compile (but I could just test it myself!). Thanks – Drammy Nov 20 '19 at 10:55
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.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
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;}
}
Andy V
  • 987
  • 11
  • 16
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);
Mahib
  • 3,977
  • 5
  • 53
  • 62
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) }
        };
    }

}
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};
mxmissile
  • 11,464
  • 3
  • 53
  • 79
pepitomb
  • 161
  • 1
  • 8
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
Talspaugh27
  • 973
  • 9
  • 16
Ahamed Ishak
  • 972
  • 11
  • 16
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 };
Eray Balkanli
  • 7,752
  • 11
  • 48
  • 82
mtngunay
  • 69
  • 1
  • 3