New Posts New Posts RSS Feed: Sorting Includes
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Sorting Includes

 Post Reply Post Reply
Author
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Topic: Sorting Includes
    Posted: 13-Aug-2009 at 11:50am
So I have a query:
var query = _context.Orders.Include("OrderDetails");

I would like to sort the Order Details within each order by the OrderDetailNumber field, which is an integer from 1 to the number of lines. Its purpose is to keep the lines in a predictable order on screen and on paper.

How do I sort the Order Details properly?
Thanks,
Simon
Back to Top
eileenv View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 15-Jun-2007
Location: United States
Posts: 68
Post Options Post Options   Quote eileenv Quote  Post ReplyReply Direct Link To This Post Posted: 14-Aug-2009 at 2:26pm
Here's an example that sorts OrderDetails by descending UnitPrice.

var query = _context.Orders.Include("OrderDetails").Select(o => new { o, SortedOrderDetails = o.OrderDetails.OrderByDescending(od => od.UnitPrice) });



Edited by eileenv - 14-Aug-2009 at 2:27pm
Back to Top
midnit View Drop Down
Senior Member
Senior Member
Avatar

Joined: 22-Jun-2009
Location: Charlotte
Posts: 112
Post Options Post Options   Quote midnit Quote  Post ReplyReply Direct Link To This Post Posted: 15-Aug-2009 at 12:49am
That returns you a list of {o, SortedOrderDetails} correct?
 
o.OrderDetails is still unsorted and I see no straight-forward way to make it so. How do you make the RelatedEntityList load sorted?
Back to Top
eileenv View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 15-Jun-2007
Location: United States
Posts: 68
Post Options Post Options   Quote eileenv Quote  Post ReplyReply Direct Link To This Post Posted: 15-Aug-2009 at 1:37am
OrderDetails is sorted. For each Order, the OrderDetails associated with that Order is sorted by UnitPrice.
Back to Top
midnit View Drop Down
Senior Member
Senior Member
Avatar

Joined: 22-Jun-2009
Location: Charlotte
Posts: 112
Post Options Post Options   Quote midnit Quote  Post ReplyReply Direct Link To This Post Posted: 19-Aug-2009 at 6:42am

I am unable to get this to work but then again LINQ and Entity are new to me. Please correct this query.

 
var q = from item in Manager.Orders
    .Include("OrderDetails").Select(o => new { o, SortedOrders = o.OrderDetails.OrderBy(od => od.UnitPrice) })
    .Include("CustomerDetails")
    orderby item.LastUpdateTms descending
    select item;
 
 
I would like the result of the query to be a list of orders sorted by lastupdatetms, and each order to have its order details sorted on unitprice. Please show me the error of my ways!
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 19-Aug-2009 at 6:59am
So I wrote this test, and it won't compile. The error is:
Error     1     Cannot implicitly convert type 'IdeaBlade.EntityModel.IEntityQuery<AnonymousType#1>' to 'IdeaBlade.EntityModel.IEntityQuery<GcsAg.DomainModel.Deal>'. An explicit conversion exists (are you missing a cast?)

What am I doing wrong? The syntax seems to be the same as eileenv's recommendation.

Here's the test:
[TestMethod]

        public void TestSortingDealPricing()
        {
            //in the database, this deal has 2 pricings
            //8052 is for 2001-4000 volume
            //8053 is for    1-2000 volume
            //these need to be sorted by volume in the deal object
            Deal deal = Deal.Fetch(10094);
            
            //first, lets assert that they aren't sorted properly
            Assert.AreEqual(2, deal.DealPricings.Count);
            Assert.AreEqual(8052, deal.DealPricings[0].DealPricingId);
            Assert.AreEqual(8053, deal.DealPricings[1].DealPricingId);

            //ok. that worked. now we need to figure out how to get those items
            //loaded and sorted properly

            //let's try a straight load
            var em = LocalEntityManager.DefaultManager;
            em.Clear();
            var qry = em.Deals
               .Include(Deal.EntityPropertyNames.DealPricings)
               .Where(item => item.DealId == 10094)
               .With(QueryStrategy.DataSourceOnly);
            Deal deal2 = qry.Execute().First();
            Assert.AreEqual(2, deal2.DealPricings.Count);
            Assert.AreEqual(8052, deal2.DealPricings[0].DealPricingId);
            Assert.AreEqual(8053, deal2.DealPricings[1].DealPricingId);
            
            //so that didn't sort them (no surprise there)
            //let's try the recommendation from eileenv
            //http://www.ideablade.com/forum/forum_posts.asp?TID=1423
            em.Clear();
            qry = em.Deals
               .Include(Deal.EntityPropertyNames.DealPricings)
               .Where(item => item.DealId == 10094)
               .Select(item => new {item, SortedDealPricings
                    = item.DealPricings.OrderByDescending(dp => dp.VolumeMin)})
               .With(QueryStrategy.DataSourceOnly);
            Deal deal3 = qry.Execute().First();
            Assert.AreEqual(2, deal3.DealPricings.Count);
            //note: the details are reversed here
            Assert.AreEqual(8053, deal3.DealPricings[0].DealPricingId);
            Assert.AreEqual(8052, deal3.DealPricings[1].DealPricingId);
        }



Edited by skingaby - 19-Aug-2009 at 7:02am
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 19-Aug-2009 at 7:11am
Interesting. Your example returns a totally different type. So I fixed my code to handle that. Now it still won't compile because the Execute is no longer returning a Deal entity. What do I need to do to get this test to pass?

em.Clear();

var qry2 = em.Deals
    .Include(Deal.EntityPropertyNames.DealPricings)
    .Where(item => item.DealId == 10094)
    .Select(item => new
    {
        item,
        SortedDealPricings
            = item.DealPricings.OrderByDescending(dp => dp.VolumeMin)
    })
    .With(QueryStrategy.DataSourceOnly);
Deal deal3 = qry2.Execute().First();


Edited by skingaby - 19-Aug-2009 at 7:13am
Back to Top
eileenv View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 15-Jun-2007
Location: United States
Posts: 68
Post Options Post Options   Quote eileenv Quote  Post ReplyReply Direct Link To This Post Posted: 19-Aug-2009 at 11:44am
Sorry I wasn't clear on how to retrieve the SortedOrderDetails.


var query = _context.Orders
.Include("OrderDetails")
.Select(o => new { o, SortedOrderDetails = o.OrderDetails.OrderByDescending(od => od.UnitPrice) });
var orderDetails = query.ToList().Select(o => o.SortedOrderDetails);


Or, here is an even better way...

Define a property in the Order class:

Order.cs:
    public IEnumerable<OrderDetail> SortedOrderDetails {
      get { return this.OrderDetails.OrderByDescending(od => od.UnitPrice); }
    }


This way you can query for Orders the regular way and return either sorted or unsorted results:

var query = _context.Orders.Include("OrderDetails");
var order = query.ToList().First();
// get sorted
var sortedOrderDetails = order.SortedOrderDetails;
// get unsorted
var unsortedOrderDetails = order.OrderDetails;


Edited by eileenv - 19-Aug-2009 at 5:56pm
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 20-Aug-2009 at 10:09am
Neither of these seems very satisfactory.

I will have to change a lot of code that already works fine if I create an Order.OrderDetailsSorted property.   I.e. All the code (and UI binding - urgh) that currently refers to OrderDetails will now have to refer OrderDetailsSorted. (Yeuch)

The second approach with
.Select(o => new { o, SortedOrderDetails = o.OrderDetails.OrderByDescending(od => od.UnitPrice) });
is only useful if I want to get the order details as a separate collection, not as part of an order, as in:
var orderDetails = query.ToList().Select(o => o.SortedOrderDetails);

The entity navigation properties are read only, right? So I can't say:
order.OrderDetails = order.OrderDetails.OrderByDesc(...)

Is there a method that will re-order the members of the collection within that collection? So instead I can say, call a sort method like this:
order.OrderDetails.Sort(...);
Back to Top
eileenv View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 15-Jun-2007
Location: United States
Posts: 68
Post Options Post Options   Quote eileenv Quote  Post ReplyReply Direct Link To This Post Posted: 20-Aug-2009 at 2:20pm
The .Select statement projects the query results into a new collection consisting of Orders and their respective OrderDetails.

Lets create a custom type so can view the result set better.

    public class MyType {
      public Order MyOrder { get; set; }
      public IEnumerable<OrderDetails> SortedOrderDetails { get; set; }
    }

The .Select statement then becomes:

.Select(o => new MyType { MyOrder = o, SortedOrderDetails = o.OrderDetails.OrderByDescending(od => od.UnitPrice) });

If you were to retrieve the first item in the collection you would see both MyOrder and its SortedOrderDetails:

MyType firstResult = query.ToList().First();
var order = firstResult.MyOrder;
var orderdetail = firstResult.SortedOrderDetails;
Back to Top
midnit View Drop Down
Senior Member
Senior Member
Avatar

Joined: 22-Jun-2009
Location: Charlotte
Posts: 112
Post Options Post Options   Quote midnit Quote  Post ReplyReply Direct Link To This Post Posted: 20-Aug-2009 at 2:44pm
So basically you get a compound collection? Whereby the SortedOrderDetails is completed unrelated to the Order except that they are at the same index in this collection.
 
IMHO this seems kludgy. Why is there an imposed limitation to sorting a RelatedEntityList? To truely do what we want, which is to receive an Order where the .OrderDetails is sorted, do we need to do a POCO query? Or is there no way to accomplish what we really want? If  having a custom object or additional property is the solution wouldn't it be just as easy (actually easier) to use a CollectionViewSource and map it to the already obtained Order.OrderDetails?
Back to Top
eileenv View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 15-Jun-2007
Location: United States
Posts: 68
Post Options Post Options   Quote eileenv Quote  Post ReplyReply Direct Link To This Post Posted: 20-Aug-2009 at 2:50pm
The query I proposed does the retrieving of the Orders and the sorting of the related OrderDetails all in a single query.

Alternatively you can just retrieve the Orders with an include of OrderDetails. Then iterate through the result set and call order.OrderDetails.OrderByDesc(...).
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 20-Aug-2009 at 4:10pm
Does order.OrderDetails.OrderByDesc(...) do the sort without having to assign it to another variable?

This fails:
Deal deal3 = qry.Execute().First();

deal3.DealPricings.OrderByDescending((x) => x.VolumeMin);
Assert.AreEqual(2, deal3.DealPricings.Count);
//note: the details are reversed here
Assert.AreEqual(8053, deal3.DealPricings[0].DealPricingId);
Assert.AreEqual(8052, deal3.DealPricings[1].DealPricingId);


Edited by skingaby - 20-Aug-2009 at 4:33pm
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 20-Aug-2009 at 4:34pm
Oh crap. I'm an idiot. I'm sorting OrderByDescending. I just need OrderBy. OOOPS!

However, this still fails:
Deal deal3 = qry.Execute().First();

deal3.DealPricings.OrderBy((x) => x.VolumeMin);
Assert.AreEqual(2, deal3.DealPricings.Count);
//note: the details are reversed here
Assert.AreEqual(8053, deal3.DealPricings[0].DealPricingId);
Assert.AreEqual(8052, deal3.DealPricings[1].DealPricingId);


While this succeeds:
var sortedPrices = deal3.DealPricings.OrderBy((x) => x.VolumeMin);

Assert.AreEqual(2, sortedPrices.Count());
//note: the details are reversed here
Assert.AreEqual(8053, sortedPrices.ElementAt(0).DealPricingId);
Assert.AreEqual(8052, sortedPrices.ElementAt(1).DealPricingId);


Edited by skingaby - 20-Aug-2009 at 5:14pm
Back to Top
midnit View Drop Down
Senior Member
Senior Member
Avatar

Joined: 22-Jun-2009
Location: Charlotte
Posts: 112
Post Options Post Options   Quote midnit Quote  Post ReplyReply Direct Link To This Post Posted: 21-Aug-2009 at 7:18am
Originally posted by midnit

To truely do what we want, which is to receive an Order where the .OrderDetails is sorted, do we need to do a POCO query? Or is there no way to accomplish what we really want?
 
Is the answer here that it cannot be done? I agree there are several ways to "work around" the problem but is there a way to solve the problem with the DESIRED results. Those results being that the actual items inside Order.OrderDetails are sorted? None of the things discussed in this thread actually answer the original question but it has not yet been said "you cant do that".
 
Can you populate Order.OrderDetails with sorted results? If so, how. If not, thank you for the info and we will work around this limitation.
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 26-Aug-2009 at 6:35am
Can you please help with this? Is there any way that we can use the Collection.Sort() method, or some other method, that actually sorts the items inside the collection, without having to return the sorted items into a different collection?
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down