Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ - filter child collection

Tags:

c#

linq

I'd like to be able to query parent entities and filter the contents of a child collection.

For example, I have a collection of OrderHeaders. I want to query this collection using LINQ to return all OrderHeaders, but I only want some of the related OrderDetail rows to be included.

I am preferably looking for a solution where I can do all of this in a single LINQ statement.

The following console app demonstrates this.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LINQ
{
    class Program
    {
        static void Main(string[] args)
        {
            List<OrderHeader> orders = GetOrderHeaders();

            var filteredOrders = from p in orders
                                 where p.Detail.Where(e => e.StockCode == "STK2").Count() > 0
                                 select p;

            foreach (var order in filteredOrders)
            {
                Console.WriteLine("Account {0} ", order.AccountCode);

                foreach (var detail in order.Detail)
                {
                    Console.WriteLine("StockCode {0}, Quantity {1}", detail.StockCode, detail.Quantity);
                }

                Console.WriteLine();
            }

            // The above will return the following:
            // Account CUST1
            // StockCode STK1, Quantity 1
            // StockCode STK2, Quantity 2
            //
            // Account CUST2
            // StockCode STK2, Quantity 1
            // StockCode STK4, Quantity 2

            // How can I get the following?
            // Account CUST1
            // StockCode STK2, Quantity 2
            //
            // Account CUST2
            // StockCode STK2, Quantity 1

            Console.ReadLine();
        }

        public static List<OrderHeader> GetOrderHeaders()
        {
            List<OrderHeader> orders = new List<OrderHeader>();

            OrderHeader header = 
                new OrderHeader { 
                    AccountCode = "CUST1", 
                    Detail = new List<OrderDetail>()};
            header.Detail.Add(
                new OrderDetail { StockCode = "STK1", Quantity = 1 });
            header.Detail.Add(
                new OrderDetail { StockCode = "STK2", Quantity = 2 });
            orders.Add(header);

            header =
                new OrderHeader
                {
                    AccountCode = "CUST2",
                    Detail = new List<OrderDetail>()
                };
            header.Detail.Add(
                new OrderDetail { StockCode = "STK2", Quantity = 1 });
            header.Detail.Add(
                new OrderDetail { StockCode = "STK4", Quantity = 2 });
            orders.Add(header);

            return orders;
        }
    }

    public class OrderHeader
    {
        public string AccountCode { get; set; }
        public List<OrderDetail> Detail {get; set;}
    }

    public class OrderDetail
    {
        public string StockCode { get; set; }
        public int Quantity { get; set; }
    }

}

Many thanks in advance for any suggestions.

Paul

like image 650
P2l Avatar asked Jan 17 '12 14:01

P2l


1 Answers

Try the following:

var filtered = orders
    .Where(o => o.Detail.Any(d => d.StockCode == "STK2"))
    .Select(o => new { Order = o, Details = o.Detail.Where(d => d.StockCode == "STK2") });

Which is then useable like so:

foreach (var entity in filtered)
{
    Console.WriteLine("Account {0} ", entity.Order.AccountCode);
    foreach (var detail in entity.Details)
    {
        Console.WriteLine("StockCode {0}, Quantity {1}", detail.StockCode, detail.Quantity);
    }
    Console.WriteLine();
}
like image 140
Rich O'Kelly Avatar answered Oct 18 '22 13:10

Rich O'Kelly