5

I have a class like this:

public class MailMessage
{
    public string From {get; set; };
    public string To {get; set; };   
    public ICollection<Attachment> Attachments { get; set; }
}

public class Attachment
{
  public string Name {get; set;} 
  public byte[] Content {get;set;}   

}

I would like to get all attachments in Attachments collection whose name ends with .pdf.

I tried the following:

List<MailMessage> list = new List<MailMessage>();
List<attachment> pdfAttachmentsCollection = list.Where(x => x.Attachments
                               .Where(attachment => 
                                attachment.Name.EndsWith(".pdf")).Select(y => y.Content));

However, this doesnt work. Any suggestions. It gives error:

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable ' to 'bool'

Gauravsa
  • 6,330
  • 2
  • 21
  • 30
  • 1
    Possible duplicate of [Difference Between Select and SelectMany](https://stackoverflow.com/questions/958949/difference-between-select-and-selectmany) – mjwills Nov 23 '18 at 04:02
  • 1
    That error is probably coming from the first where since it isn't a boolean expression for the predicate that is given. – Jonathan Van Dam Nov 23 '18 at 04:09
  • 2
    `var pdfAttachmentsCollection = list.SelectMany(x => x.Attachments.Where(a => a.Name.ToLower().EndsWith(".pdf"))).ToList();` – Jimi Nov 23 '18 at 04:11
  • 1
    I'd suggest starting with `list.SelectMany(x => x.Attachments)`. The rest is standard `Where` stuff that you'll work out easily. _Jim's approach will work equally well._ – mjwills Nov 23 '18 at 04:12
  • 1
    Better for case-insensitive check: `Name.EndsWith(".pdf", StringComparison.InvariantCultureIgnoreCase)` – gregmac Nov 23 '18 at 04:21

1 Answers1

10

A couple issues:

  • You need to SelectMany from the MailMessage object to get all attachments in a single collection
  • You're selecting y.Content which is byte[] but trying to get a list of Attachment
  • LINQ returns IEnumerables, not Lists

Rewritten, you probably want to do something like:

List<MailMessage> list = new List<MailMessage>();
IEnumerable<Attachment> pdfAttachmentsCollection = 
    list.SelectMany(message => 
        message.Attachments
            .Where(attachment => attachment.Name.EndsWith(".pdf")));

This is also a good candidate to write as a query expression:

IEnumerable<Attachment> pdfAttachmentsCollection = 
    from message in list 
    from attachment in message.Attachments
    where attachment.Name.EndsWith(".pdf")
    select attachment;

Both of these do the same thing, just with different syntax.

If you want this as a List<Attachment> you can also do .ToList() at the end of either query. As an IEnumerable, it won't be evaluated until you start iterating through it (and depending on how you use it, you might never have the full collection in memory at any time). However, if you do multiple iterations or operations on it, it'll cause it to be evaluated multiple times -- so in those cases .ToList() makes sense for sure.

gregmac
  • 24,276
  • 10
  • 87
  • 118