OrderBy()。ThenBy()。ThenBy()没有在实体列表上给出预期的结果

时间:2016-02-29 01:47:27

标签: c# linq

无法理解为什么我的简单排序不能按预期工作。这是违规行:

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

最后两个排序(紧急和时间戳)无法正常工作。我的结果可以通过IsUrgent或Timestamp排序,但不能同时排序。

我的最终结果应该是所有消息都按类型排序(其中只有3种类型是New,Saved,Deleted)。然后在每种类型中,首先有紧急消息,然后按时间戳排序。

更新

你要求提供更多信息,所以这里是

[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));
    }

这里是所有魔法/混乱发生的地方

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();
        }
    }

3 个答案:

答案 0 :(得分:3)

理论上它应该按预期工作。您可以使用以下方式跟踪查询:

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();

答案 1 :(得分:2)

所以,现在混乱的根源很明显。当你对布尔进行排序时,首先得到false值,然后得到true个值。所以TS的配方要么是使用

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

return messages.OrderBy(o => o.MessageType)
               .Then(p => !p.IsUrgent)
               .ThenByDescending(p => p.Timestamp)
               .ToList();
Imo第一种方式更清晰,更直观,但这取决于你。

答案 2 :(得分:1)

快速调试显示这不是linq2objects的情况。

测试代码:

void Main()
{
    var messages = GenerateTestData();  
    var results = messages.OrderBy(o => o.Type)
               .ThenBy(p => p.IsUrgent)
               .ThenByDescending(p => p.TimeStamp)
               .ToList();          
    foreach (var m in results)
    {
        Console.WriteLine(m.ToString());
    }
}

public List<Message> GenerateTestData()
{
    var result = new List<Message>(10){

    new Message(){ Type = MessageType.New, IsUrgent = true, TimeStamp = DateTime.Now.AddMilliseconds(10).Ticks},
    new Message(){ Type = MessageType.Saved, IsUrgent = true, TimeStamp = DateTime.Now.AddMilliseconds(9).Ticks},
    new Message(){ Type = MessageType.Deleted, IsUrgent = false, TimeStamp = DateTime.Now.AddMilliseconds(8).Ticks},
    new Message(){ Type = MessageType.Deleted, IsUrgent = false, TimeStamp = DateTime.Now.AddMilliseconds(7).Ticks},
    new Message(){ Type = MessageType.Saved, IsUrgent = false, TimeStamp = DateTime.Now.AddMilliseconds(6).Ticks},
    new Message(){ Type = MessageType.New, IsUrgent = true, TimeStamp = DateTime.Now.AddMilliseconds(5).Ticks},
    new Message(){ Type = MessageType.Saved, IsUrgent = true, TimeStamp = DateTime.Now.AddMilliseconds(4).Ticks},
    new Message(){ Type = MessageType.Saved, IsUrgent = false, TimeStamp = DateTime.Now.AddMilliseconds(3).Ticks},
    new Message(){ Type = MessageType.Deleted, IsUrgent = true, TimeStamp = DateTime.Now.AddMilliseconds(2).Ticks},
    new Message(){ Type = MessageType.New, IsUrgent = false, TimeStamp = DateTime.Now.AddMilliseconds(1).Ticks}
    };
    return result;
}

public enum MessageType
{
    New,
    Saved,
    Deleted
}
public class Message
{
    public MessageType Type{get;set;}
    public bool IsUrgent{get;set;}
    public long TimeStamp {get;set;}

    public  override string ToString()
    {
        return string.Format("Type: {0}, IsUrgent: {1}, TimeStamp: {2}",this.Type, this.IsUrgent, this.TimeStamp);
    }
}

导致以下完全正确和预期的结果

Type: New, IsUrgent: False, TimeStamp: 635922806855227531
Type: New, IsUrgent: True, TimeStamp: 635922806855317531
Type: New, IsUrgent: True, TimeStamp: 635922806855267531
Type: Saved, IsUrgent: False, TimeStamp: 635922806855277531
Type: Saved, IsUrgent: False, TimeStamp: 635922806855247531
Type: Saved, IsUrgent: True, TimeStamp: 635922806855307531
Type: Saved, IsUrgent: True, TimeStamp: 635922806855257531
Type: Deleted, IsUrgent: False, TimeStamp: 635922806855297531
Type: Deleted, IsUrgent: False, TimeStamp: 635922806855287531
Type: Deleted, IsUrgent: True, TimeStamp: 635922806855237531

我假设IsUrgentboolTimeStamplong。 所以,我们肯定需要TS的更多细节。