linqentity-framework

LINQ include and Projection


I have some classes defining entities with relationships

Account 
has many Conversations [IEnumerable<Conversation> Conversations]

Conversation 
has many Participants [IEnumerable<Account> Participants]
has many Messages [IEnumerable<Message> Messages]

Message 
has one Sender [Account Sender]
has one Conversation [Conversation Conversation]

I'm trying to write a LINQ query that returns a list of Conversation ordered by date and including related participants and messages.

public async Task<List<Conversation>> FindAllByAccountIdAsync(Int32 id)
{
    return await _Db.Conversations
         .Where(c => c.Participants.Any(p => p.AccountId == id))
         .Include(c => c.Participants)
         .Include(c => c.Messages)
         .ToListAsync();
}

This do the work but includes to much data i do not really need.

public async Task<List<Conversation>> FindAllByAccountIdAsync(Int32 id)
{
    return await _Db.Conversations
         .Where(c => c.Participants.Any(a => a.AccountId == id))
         .Include(c => c.Participants.Select(a=> new 
                       {
                          AccountId = a.AccountId,
                          Profile = new { FullName = a.Profile.FullName,
                                          Email = a.Profile.Email
                                        }                        
                       }))
         // Only return the last message in 
         // Eventually I would not return an array with a single object but just the single object inside associated with the property LastMessageIn
         .Include(c => c.Messages.OrderBy(m => m.Date).Select(m=> new 
                       {
                          Body = m.Body,
                          SenderId = m.Sender.AccountId
                       }).Last())
         .ToListAsync();
}

This script returns a mile long exception

{"message":"An error has occurred.","exceptionMessage":"The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties........}

My mind resist understanding and learning LINQ I do not know if its just me but as soon requirements exceeds basic querying and projection it break out of my control

Someone have some hints?


Solution

  • I'm not sure if I understand your question, but I believe you want something like this:

    public async Task<List<Conversation>> FindAllByAccountIdAsync(Int32 id)
    {
        return await _Db.Conversations
             .Where(c => c.Participants.Any(p => p.AccountId == id))
             .Include(c => c.Participants)
             .Include(c => c.Messages)
             .Select(c => new 
             {
                Participants = c.Participants.Select(a=> new 
                           {
                              AccountId = a.AccountId,
                              Profile = new { FullName = a.Profile.FullName,
                                              Email = a.Profile.Email
                                            }                        
                           },
                 //EDIT: using OrderByDescending and FirstOrDefault
                 Messages = c.Messages.OrderByDescending(m => m.Date).Select(m=> new 
                           {
                              Body = m.Body,
                              SenderId = m.Sender.AccountId
                           }).FirstOrDefault())
                 //others properties here
             }
             .ToListAsync();
    }