0

[Explanation]

I need to create a class to send e-mails through a provider (SendGrid or Mandrill, for now).

The project is divided, for now, in "Email Core" and a "Provider Core", and those interfaces has been created:

public interface IEmail
{
    ICollection<IContact> Recipients { get; }
    IContact From { get; set; }
    string EmailBody { get; set; }
    string Subject { get; set; }
    IContact ReplyTo { get; set; }
    ICollection<IContact> Attachments { get; }
}

And it's dependencies

public interface IContact
{
    string Name { get; set; }
    string Email { get; set; }
}

public interface IAttachment
{

    string FileName { get; set; }
    Stream Content { get; set; }
    string StringContent { get; }
    string ContentType { get; set; }
    string FilePath { get; set; }
}

Then, I've created an object "Email" to assembly an e-mail with all its caracteristics:

public class Email : IEmail
{
    #region Constructors
    public Email()
    {

    }
    #endregion

    #region Private Properties
    private readonly ICollection<Contact> _recipients = new List<Contact>();
    private readonly ICollection<Attachment> _attachments = new List<Attachment>();
    #endregion

    #region Public Properties
    public ICollection<IContact> Recipients
    { 
         get 
         {
             return (ICollection<IContact>)_recipients;
         }
     }

    public IContact From
    {
        get;
        set;
    }

    public string EmailBody
    {
        get;
        set;
    }

    public string Subject
    {
        get;
        set;
    }

    public IContact ReplyTo
    {
        get;
        set;
    }

    public ICollection<IAttachment> Attachments
    {
        get { return (ICollection<IAttachment>)_attachments ;}
    }
    #endregion

    #region Private Methods

    #endregion

    #region Public Methods
    public Email AddRecipient(IContact Recipient)
    {
        _recipients.Add((Contact)Recipient);
        return this;
    }

    public Email SetFrom(IContact from)
    {
        From = from;
        return this;
    }

    public Email SetEmailBody(string body)
    {
        EmailBody = body;
        return this;
    }

    public Email SetSubject(string subject)
    {
        Subject = subject;
        return this;
    }

    public Email SetReplyTo(IContact replyto)
    {
        ReplyTo = replyto;
        return this;
    }

    public Email AddAttachment(IAttachment attachment)
    {
        _attachments.Add((Attachment)attachment);
        return this;
    }
    #endregion
}

Finally, I've a class to create the "Provider" object, whose will consume the "Email" object and then deliver it:

public class ProviderSendGrid : IProvider
{
    #region Constructors
    public ProviderSendGrid(string SendGridUser, string SendGridPassword)
    {
        _networdcredential = new NetworkCredential(SendGridUser, SendGridPassword);
        _sendgridweb = new Web(_networdcredential);
    }
    #endregion

    #region Propriedades Privadas
    private NetworkCredential _networdcredential;
    private SendGridMessage _message;
    private Web _sendgridweb;
    #endregion

    #region Public Properties

    #endregion

    #region Private Methods
    /// <summary>
    /// Verifica se string é e-mail
    /// </summary>
    /// <param name="Email">String que se deseja verificar se é e-mail ou não.</param>
    /// <returns>Retorna verdadeiro caso a string informada seja um e-mail e falso caso contrário.</returns>
    private bool IsValidEmail(string Email)
    {
        try
        {
            var address = new MailAddress(Email);
            return address.Address == Email;
        }
        catch
        {
            return false;
        }
    }

    private List<string> FormatedContacts(ICollection<IContact> Recipients)
    {
        if (Recipients == null)
            throw new Exception("Recipients parameter on Recipients method can't be null.");

        List<string> lstRet = new List<string>();

        Parallel.ForEach(Recipients, item =>
        {
            if (!IsValidEmail(item.Email))
                throw new Exception("Invalid e-mail informed.", new Exception("The following e-mail is not valid: " + item.Email));

            lstRet.Add(item.Name + " <" + item.Email + ">");
        });

        return lstRet;
    }
    #endregion

    #region Public Methods
    /// <summary>
    /// 
    /// </summary>
    /// <param name="Email">Objeto que implemente a interface MKM.Email.Core.Interfaces.IEmail</param>
    public async void Send(IEmail Email)
    {
        if (string.IsNullOrEmpty(Email.EmailBody) || string.IsNullOrEmpty(Email.Subject))
            throw new Exception("Email body or subject is null.");

        if (Email.From == null)
            throw new Exception(@"The property ""From"" can't be null.");

        //Compondo a mensagem
        _message = new SendGridMessage();

         //Stackoverflow Forum: The error occours in the following line, when I try to get the "Recipients" property from "Email" object
        _message.AddTo(FormatedContacts(Email.Recipients));
        _message.From = new MailAddress(Email.From.Email, Email.From.Name);
        _message.Subject = Email.Subject;
        _message.Html = Email.EmailBody;

        Parallel.ForEach(Email.Attachments, item => 
        {
            _message.AddAttachment(item.Content, item.FileName);
        });

        _message.ReplyTo = new MailAddress[]{new MailAddress(Email.ReplyTo.Email, Email.ReplyTo.Name)};

        await _sendgridweb.DeliverAsync(_message);
    }
    #endregion
}

[Problem]

In the "ProviderSendGrid" class, in the "Send" method, when I try to get the "Recipients" property, I receive the following error:

Additional information: Unable to cast object of type 'System.Collections.Generic.List1[Email.Core.Contact]' to type 'System.Collections.Generic.ICollection1[Email.Core.Interfaces.IContact]'.

Why this happens if the "Email.Recipients" property returns an "List". List implements ICollection and Contact implements IContact.

I'm not sure if my explanation was clear enough, but if I didn't, please let me know.

Thanks for your attention. Best regards!

  • You cannot safely return a Collection as a Collection because you are not allowed to add an object U : InterfaceOnT to a Collection. See the duplicate for covariance/contravariance discussion. – Tetsujin no Oni Mar 27 '15 at 20:13

2 Answers2

1

In the ProviderSendGrid class the method FormatedContacts should be taking an ICollection of IContact not Contact

private List<string> FormatedContacts(ICollection<IContact> Recipients)

Also check the Email Class. It has an ICollection of Contact for _recipients.

private readonly ICollection<IContact> _recipients = new List<IContact>();

Finally, remove the Cast to Contact when adding the recipient.

#region Public Methods
public Email AddRecipient(IContact Recipient)
{
    _recipients.Add(Recipient);
    return this;
}
Praveen Paulose
  • 5,741
  • 1
  • 15
  • 19
  • Sorry Praveen, I forgot to change this before post, my code is like you suggest but I'm still getting the error, which occours in the "Email" class, in the get of the Recipients property: get { return (ICollection)_recipients; } – Leandro Camargo Mar 27 '15 at 20:35
  • Praveen, if I change Contact to IContact in the Email class, I get the following error: Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.ICollection'. An explicit conversion exists (are you missing a cast?) – Leandro Camargo Mar 27 '15 at 21:11
  • Sorry about that. It should have been IContact. Updated the answer. – Praveen Paulose Mar 27 '15 at 21:19
  • Thanks you so much Praveen, this change works on my code! – Leandro Camargo Mar 27 '15 at 22:19
0

observe following line: _message.AddTo(FormatedContacts(Email.Recipients))

IEmail.Recipients code line: ICollection<IContact> Recipients { get; }

from function definition: private List<string> FormatedContacts(ICollection<Contact> Recipients)

The function expects ICollection<Contact>, you are sending in ICollection<IContact>

Daniel MesSer
  • 1,191
  • 1
  • 7
  • 13
  • Sorry Daniel, I forgot to change this before post, my code is like you suggest but I'm still getting the error, which occours in the "Email" class, in the get of the Recipients property: get { return (ICollection)_recipients; } – Leandro Camargo Mar 27 '15 at 20:37