简体   繁体   中英

Grouping by when id can be in column 1 or 2

On my website, members can send messages to each other. Here is my Message class :

public partial class Message
{
    public System.Guid Id { get; set; }
    public System.DateTime DateMsg { get; set; }
    public string SenderId { get; set; }
    public string ReceiverId { get; set; }
    public string Text { get; set; }
}

I'd like to do a query that get the last message for each opened discussion. Since I have no "discussion id", I can't do "group by discussionId" and then select the first one in each group. How could I do ?

public IEnumerable<Message> Get_DiscussionsFor(string userId)
        {
            List<Message> list;
            using (var context = new ModelContainer())
            {
                IQueryable<Message> dbQuery = context.Set<Message>();               

                list = dbQuery
                    .AsNoTracking()
                    .Where(m => m.SenderId.Equals(userId) || m.ReceiverId.Equals(userId))
                    .OrderByDescending(m => m.DateMsg)                        
                    .ToList<Message>();
            }
            return list;
        }

Well, this might work, although it won't be fast.

Here's the first part of your query:

            var list1 = dbQuery
                .AsNoTracking()
                .Where(m => m.SenderId.Equals(userId) || m.ReceiverId.Equals(userId));

I am going to create another query, that creates an anonymous type encapsulating the message and the ID of the other user.

            var senderFirst = list1
                .Where(m => m.SenderId.Equals(userId))
                .Select(
                 m => {
                   Message = m,
                   OtherUser = m.ReceiverId
                 });

I'm creating another one, this time for the ones where the user we want is the receiver, and the Other User is the Sender:

          var receiverFirst = list1
              .Where(m => m.ReceiverId.Equals(userId))
              .Select(
                m => {
                   Message = m,
                   OtherUser = m.SenderId,
                });

I am now concatenating them. I can do this because they have exactly the same properties with the same types and same names: behind the scenes, they're the same anonymous type.

            var list2 = senderFirst.Concat(receiverFirst);

As a result, each message involving the user you want will appear with the other user identified as OtherUser . Note that we haven't changed the Message object at all: it's still there, with the right values for SenderId and ReceiverId . We just wrapped it in another object, with additional metadata.

You said that any message involving a given two users is in the same discussion. So, we can now simulate grouping by discussion by grouping by the OtherUser :

             var list3 = list2.GroupBy(m => m.OtherUser);

So now we have grouped messages. You can now sort each group and select the final message.

             var list4 = list3
                 .Select(g => g.OrderByDescending(m => m.Message.DateMsg).First());

And now you have your List<Message> , because your Message object is still in there, ready to be sprung from encapsulation:

             var list = list4
                 .Select(m => m.Message)
                 .ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM