Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OrderBy().ThenBy().ThenBy() not giving expected result on a List of entities

Tags:

c#

linq

Having trouble understanding why my simple ordering doesn't work as expected. Here's the offending line:

return messages.OrderBy(o => o.MessageType)
               .ThenBy(p => p.IsUrgent)
               .ThenByDescending(p => p.Timestamp)
               .ToList();

The last two orderings (Urgent and Timestamp) don't work correctly. My results are either ordered by IsUrgent or Timestamp, but never both.

My end result should be all messages are sorted by type (there's only 3 types New, Saved, Deleted). Then in each type, have urgent messages first, and then order by the timestamp.

UPDATE

You asked for more info, so here it is

[TestMethod]
    public void Messages_should_sort_correctly()
    {
        var contact = new Models.Contact(); //just to satisfy the object

        var expectedOrder = new string[] { "5", "6", "12", "3", "10", "9", "4", "8", "11", "13", "2", "7", "1" };
        var messages = new List<IMessage>
        {
            new Models.Message { FileName = "1",   MessageType = MessageType.Deleted,   Timestamp = 31,  Contact = contact },
            new Models.Message { FileName = "2",   MessageType = MessageType.Deleted,   Timestamp = 34,  Contact = contact },
            new Models.Message { FileName = "3",   MessageType = MessageType.Inbox,     Timestamp = 11,  Contact = contact },
            new Models.Message { FileName = "4",   MessageType = MessageType.Saved,     Timestamp = 25,  Contact = contact },
            new Models.Message { FileName = "5",   MessageType = MessageType.Inbox,     Timestamp = 14,  Contact = contact, IsUrgent = true },
            new Models.Message { FileName = "6",   MessageType = MessageType.Inbox,     Timestamp = 13,  Contact = contact, IsUrgent = true },
            new Models.Message { FileName = "7",   MessageType = MessageType.Deleted,   Timestamp = 32,  Contact = contact },
            new Models.Message { FileName = "8",   MessageType = MessageType.Saved,     Timestamp = 22,  Contact = contact },
            new Models.Message { FileName = "9",   MessageType = MessageType.Saved,     Timestamp = 23,  Contact = contact, IsUrgent = true },
            new Models.Message { FileName = "10",  MessageType = MessageType.Saved,     Timestamp = 24,  Contact = contact, IsUrgent = true },
            new Models.Message { FileName = "11",  MessageType = MessageType.Saved,     Timestamp = 21,  Contact = contact },
            new Models.Message { FileName = "12",  MessageType = MessageType.Inbox,     Timestamp = 12,  Contact = contact },
            new Models.Message { FileName = "13",  MessageType = MessageType.Deleted,   Timestamp = 33,  Contact = contact, IsUrgent = true }
        };

        messageServiceMock.Setup(m => m.GetAllMessagesAsync()).Returns(Task.FromResult(messages as IList<IMessage>)).AtMostOnce();

        var result = service.Messages; //this property returns the messages from our mock, and then sorts and orders
        var actualOrder = result.Select(m => m.FileName);


        //expected order "5", "6", "12", "3", "10", "9", "4", "8", "11", "13", "2", "7", "1"
        //actual order   "12", "3", "5", "6", "4", "8", "11", "10", "9", "2", "7", "1", "13" 
        Assert.IsTrue(actualOrder.SequenceEqual(expectedOrder));
    }

And here is where all the magic/confusion happens

private List<Domain.Interfaces.IMessage> messages = new List<Domain.Interfaces.IMessage>();
    public IList<Domain.Interfaces.IMessage> Messages
    {
        get
        {
            if (!messages.Any())
            {
                messages = messageService.GetAllMessagesAsync().Result.ToDomain().ToList();
            }

            return messages.OrderBy(o => o.MessageType).ThenBy(p => p.IsUrgent == true).ThenByDescending(p => p.Timestamp).ToList();
        }
    }
like image 655
Matt Avatar asked Feb 07 '23 15:02

Matt


1 Answers

Theoretically it should work as expected. You can trace your query using:

IQueryable<Message> query = messages
                           .OrderBy(o => o.MessageType)
                           .ThenBy(p => p.IsUrgent)
                           .ThenByDescending(p => p.Timestamp);
//debug query.ToString() to see the generated sql
//should be SELECT XXX WHERE YYY ORDER BY MessageType, IsUrgent, Timestamp DESC
return query.ToList();
like image 127
Cheng Chen Avatar answered Feb 16 '23 02:02

Cheng Chen