Sandip D
Sandip D

Reputation: 121

How to use Group By with Join and Min in Linq

I have two tables.

Order:

Product:

I want to get the list of all product with earliest delivery date.

I am getting correct data by using following sql. This gives me list of all products with earliest delivery date.

SELECT 
    p.Name, p.Description, min(o.DeliveryDate)
FROM Product p
JOIN Order o
On o.ProductID = p.ID
Group By p.ID;

I have tried to write it using Linq but something is missing. I am not able to identify how can I write this query.

I have tried out relative stack overflow solutions but not helped in my case.

My Linq is :

await (from p in dbContext.Product
  join o in dbContext.Order
  on o.ProductID equals p.ID
  select new
  {
      p.ID,
      p.Name,
      p.Description,
      o.DeliveryDate
  }).GroupBy(g => g.ID).ToListAsync();

It gives me data after join and group by. Where should I put Min in the Linq to get the Earliest DeliveryDate for the Product?

Upvotes: 0

Views: 2138

Answers (3)

Jeffrey Ferreiras
Jeffrey Ferreiras

Reputation: 271

If you're trying to order your groups you can use the "OrderBy" linq method.

Apply the following after the .GroupBy()

.OrderBy(group => group.First().DeliveryDate).ToListAsync();

Upvotes: 0

BlueMonkMN
BlueMonkMN

Reputation: 25601

I used some classes to stand in for the database, but does this work?

class Product
{
  public int ID;
  public string Name;
  public string Description;
}

class Order
{
  public int ProductID;
  public DateTime DeliveryDate;
}

class Program
{
  static void Main(string[] args)
  {
     Product[] proTestData = new Product[]
     {
        new Product() {ID=1, Name="Widget", Description="Banded bulbous snarfblat"},
        new Product() {ID=2, Name="Gadget", Description="Hitchhiker's guide to the galaxy"}
     };
     Order[] ordTestData = new Order[]
     {
        new Order() {ProductID=1, DeliveryDate=new DateTime(2017,12,14)},
        new Order() {ProductID=1, DeliveryDate=new DateTime(2017,12,20)},
        new Order() {ProductID=2, DeliveryDate=new DateTime(2017,12,23)},
        new Order() {ProductID=2, DeliveryDate=new DateTime(2017,12,22)},
        new Order() {ProductID=2, DeliveryDate=new DateTime(2017,12,21)},
        new Order() {ProductID=1, DeliveryDate=new DateTime(2017,12,18)}
     };

     var rows =
        (from p in proTestData
         join o in ordTestData
         on p.ID equals o.ProductID
         group o.DeliveryDate by p into grp
         select new {
            grp.Key.ID,
            grp.Key.Name,
            grp.Key.Description,
            minDate = grp.Min()}
        ).ToList();

     foreach (var row in rows)
     {
        Console.WriteLine("{0}: {1}", row.Name, row.minDate);
     }
  }
}

The output is

Widget: 12/14/2017 12:00:00 AM
Gadget: 12/21/2017 12:00:00 AM

Upvotes: 1

Gert Arnold
Gert Arnold

Reputation: 109079

I assume you have a navigation property Product.Orders. If not, it's highly recommended to add it to your code. Having that property, this will achieve what you want:

from p in dbContext.Product
select new
{
    p.ID,
    p.Name,
    p.Description,
    MinDeliveryDate = (DateTime?)o.Orders.Min(o => o.DeliveryDate)
}

The cast to DateTime? is to prevent exceptions when products don't have orders.

If for some reason you don't have the navigation property and really can't add it at the moment you can use:

from p in dbContext.Product
select new
{
    p.ID,
    p.Name,
    p.Description,
    MinDeliveryDate = (DateTime?)dbContext.Order
                                          .Where(o => o.ProductID == p.ID)
                                          .Min(o => o.DeliveryDate)
}

Note that by starting the query at Product and not joining with Order you don't need the grouping any more.

Upvotes: 1

Related Questions