0

I am trying to iterate over a list of OrderItems and associate the results with my Order Element. I was able, through the help of this forum, to make the association work "correctly" when there is only one OrderItem under my Order Element. But I have not been able to get to work when there are multiple OrderItems under my Order Element.

I have looked at a lot of threads that show a .ForEach(x => syntax and have seen the ToList() suggested but I didn't find any similar enough to what I was doing to figure out what I need to do. I am pretty sure that I need the .ForEach around my

OrderItems = new List<OrderItems>()

but I am not sure how to apply it.

Edit To clarify my question - The XML below has two orders in it. The first order has one item, which is the Silver Widget. The second order has two items, which are the Gold and the Blue Widget. However when I run this program I am only getting the first item of order #2. I am really asking how do I get both of them to show under the order?

ID: 1 OrderDate: 7/7/2014 12:00:00 AM OrderTotal: 12.99
        ProductName: Silver Widgets
        Price: 12.99
        Quantity: 1
ID: 2 OrderDate: 7/7/2014 12:00:00 AM OrderTotal: 20.00
        ProductName: Gold Widgets
        Price: 10.00
        Quantity: 1

The above is the result of the Console.WriteLines in the code..

    static void Main()
    {
        XDocument document = XDocument.Parse(GetXml());

        var query = from el in document.Root.Elements("Order")
                    select new Orders
                    {
                        Id = (int)el.Element("Id"),
                        OrderDate = (DateTime)el.Element("OrderDate"),
                        OrderTotal = (Decimal)el.Element("OrderTotal"),

                        OrderItems = new List<OrderItems>()
                        {
                            new OrderItems()
                            { 
                                ProductName = (string)el.Element("OrderItems").Element("OrderItem").Element("ProductName"), 
                                Price = (Decimal)el.Element("OrderItems").Element("OrderItem").Element("Price"),
                                Quantity = (int)el.Element("OrderItems").Element("OrderItem").Element("Quantity")
                            }
                        }
                    };

        foreach (var cc in query)
        {
            Console.WriteLine(String.Format("ID: {0} OrderDate: {1} OrderTotal: {2}"
                , cc.Id
                , cc.OrderDate
                , cc.OrderTotal));
            foreach (var xx in cc.OrderItems)
            {
                Console.WriteLine("\tProductName: " + cc.OrderItems[0].ProductName);
                Console.WriteLine("\tPrice: " + cc.OrderItems[0].Price);
                Console.WriteLine("\tQuantity: " + cc.OrderItems[0].Quantity);
            }
        }

        Console.ReadLine();

    }

    public static String GetXml()
    {
        return @"<?xml version='1.0' encoding='utf-8' ?>
        <OrderXml>
          <Order>
            <Id>1</Id>
            <OrderDate>7/7/2014</OrderDate>
            <OrderTotal>12.99</OrderTotal>
            <OrderItems>
              <OrderItem>
                <ProductName>Silver Widgets</ProductName>
                <Price>12.99</Price>
                <Quantity>1</Quantity>
              </OrderItem>
            </OrderItems>
          </Order>
          <Order>
            <Id>2</Id>
            <OrderDate>7/7/2014</OrderDate>
            <OrderTotal>20.00</OrderTotal>
            <OrderItems>
              <OrderItem>
                <ProductName>Gold Widgets</ProductName>
                <Price>10.00</Price>
                <Quantity>1</Quantity>
              </OrderItem>
              <OrderItem>
                <ProductName>Blue Widgets</ProductName>
                <Price>10.00</Price>
                <Quantity>1</Quantity>
              </OrderItem>
            </OrderItems>
          </Order>
        </OrderXml>";
    }
Buck Hicks
  • 1,534
  • 16
  • 27

3 Answers3

3

You don't want to use ForEach (or foreach) here at all.

You have an XElement that represents your OrderItems element. It has some number of OrderItem sub-elements. What you need to do is get the collection of those sub elements (which you can do using the Elements method) and transform that collection into a collection of your custom items. Transforming a sequence of items is a job for Select, not ForEach.

OrderItems = el.Element("OrderItems")
    .Elements("OrderItem")
    .Select(orderItem => new OrderItem()
    {
        ProductName = (string)orderItem.Element("ProductName"),
        Price = (decimal)orderItem.Element("Price"),
        Quantity = (int)orderItem.Element("Quantity"),
    })
    .ToList(),
Servy
  • 202,030
  • 26
  • 332
  • 449
  • I know this works because my foreach loop is showing the first item on Order #2 twice, which I also understand is doing that because that is what my loop is telling it to do. I should be able to get the piece corrected myself though. So Thanks! – Buck Hicks Jul 08 '14 at 18:11
0

Adding a ForEach "operator" to LINQ is a common user extension, but it is not in LINQ by default.

The usual approach is to greedily iterate over each value calling a delegate which does not return a result.

While the iteration is similar, this has no other relation to the foreach keyword.

Richard
  • 106,783
  • 21
  • 203
  • 265
0

Microsoft has decided not to implement .ForEach extension for IEnumerable

But you can implement your own:

public static class Extensions
{
    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (action == null) throw new ArgumentNullException("action");

        foreach (T item in source)
        {
            action(item);
        }
    }
}

Works like a charm.

Plus, you need to implement the other advice, from @Servy.

Darek
  • 4,687
  • 31
  • 47
  • 1
    I could be wrong, but it doesn't sound like this is what he's asking. Title doesn't seem to match the question – Kyle Gobel Jul 08 '14 at 17:55
  • Sorry when I asked the question I thought I needed a LINQ ForEach, which is why I worded it that way. Should I change the question now to match or leave it as is? – Buck Hicks Jul 08 '14 at 18:07