0

I have following class inherent from ObservableCollection:

public class Emails : ObservableCollection<Email>
    {
        public Emails()
        {
        }

        public Emails(IEnumerable<Email> emails)
        {
            foreach (var e in emails)
            {
                //   Will throw System.FormatException:
                //     address is not in a recognized format.
                var addr = new System.Net.Mail.MailAddress(e.Address);
                Add(e);
            }
        }

        public IEnumerable<Email> GetAll()
        {
            return Items;
        }

    }

My question is, before an element will be added, how can I validate the item? I will validate, if the mail is in the right format.

DeshDeep Singh
  • 1,817
  • 2
  • 23
  • 43
softshipper
  • 32,463
  • 51
  • 192
  • 400

4 Answers4

2

There are many threads here and on other forums about the problems with email validation. The general consensus is: don't. Especially don't use regular expressions to do it.

One valid way is to send an eMail to the address and check whether that succeeds.

On the other hand your code comments already give a hint about an exception being thrown. If you change the code as follows, you can only add valid addresses:

public Emails(IEnumerable<Email> emails)
{
        List<Email> invalid = new List<Email>();

        foreach (var e in emails)
        {
            //   Will throw System.FormatException:
            //     address is not in a recognized format.
            try
            {
                var addr = new System.Net.Mail.MailAddress(e.Address);
                Add(e);
            }
            catch (FormatException)
            {
                // The address was invalid. Add to a list of invalid
                // addresses
                invalid.Add(e);
            }
        }
}

This generates a list of invalid addresses and adds the valid addresses to your collection.

Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • Why we shouldn't use Regex for validate email? – ivamax9 Jul 20 '15 at 11:37
  • Note that using try/catch to control the programm flow is always bad practice. It's slow and made for handling exceptions, while a missing validation is not an exception, but missing code. – LInsoDeTeh Jul 20 '15 at 11:38
  • @Chase Because it is virtually impossible to create a regular expression that checks all RFC compliant email addresses. – Thorsten Dittmar Jul 20 '15 at 11:38
  • @LInsoDeTeh Feel free to provide a **proper** way of validating. The constructor of `MailAddress` already does some validation. It may throw an exception upon failures, so it is perfectly valid to catch that exception. I'm not propagating to use this for *validation*. I'm saying: Don't validate at all, but handle invalid addresses properly. The regular expression you provided does not handle all valid eMail-addresses, so it is not a proper way of validating. – Thorsten Dittmar Jul 20 '15 at 11:39
  • I mean before Add a new email to the collection with ADD method, then it should validate. – softshipper Jul 20 '15 at 11:44
  • I know what you mean. The code above rejects invalid email addresses and only adds those that are valid from the `MailAddress` constructor's point of view. If you need additional validation in the `Add` method itself, you need to override that method and do the same thing as above (try to create `MailAddress` and have it throw an exception if it can't). – Thorsten Dittmar Jul 20 '15 at 11:47
0

You can check with a regex:

Regex emailRegex = new Regex(@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$");
if (emailRegex.IsMatch(e.Address) {
     //valid eMail address
}
LInsoDeTeh
  • 1,028
  • 5
  • 11
  • 1
    No. You can not. The only proper regular expression to check for valid email addresses is found here: http://ex-parrot.com/~pdw/Mail-RFC822-Address.html. Also, read this: http://davidcel.is/posts/stop-validating-email-addresses-with-regex/ – Thorsten Dittmar Jul 20 '15 at 11:35
0

You should create a new version of Add, SetItem, and InsertItem:

public class Emails : ObservableCollection<Email> {
    public Emails() {
    }

    public Emails(IEnumerable<Email> emails) {
        foreach (var e in emails) {
            Add(e);
        }
    }

    public IEnumerable<Email> GetAll() {
        return Items;
    }

    protected bool InsertItem(int index, Email item, bool throwOnInvalidEmailAddress = false) {
        if (IsValidEmailAddress(item.Address)) {
            base.InsertItem(index, item);
            return true;
        }
        if (throwOnInvalidEmailAddress)
            throw new Exception("Bad email address!");
        return false;
    }

    protected bool SetItem(int index, Email item, bool throwOnInvalidEmailAddress = false) {
        if (IsValidEmailAddress(item.Address)) {
            base.SetItem(index, item);
            return true;
        }
        if (throwOnInvalidEmailAddress)
            throw new Exception("Bad email address!");
        return false;
    }

    public bool Add(Email item, bool throwOnInvalidEmailAddress = false) {
        if (IsValidEmailAddress(item.Address)) {
            base.Add(item);
            return true;
        }
        if (throwOnInvalidEmailAddress)
            throw new Exception("Bad email address!");
        return false;
    }

    private static readonly Regex EmailValidatorRegex = 
        new Regex(@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$");
    private bool IsValidEmailAddress(string address) {
        return !string.IsNullOrWhiteSpace(address) && EmailValidatorRegex.IsMatch(address);
    }
}

Note 1: The used Regex came from @LInsoDeTeh 's answer

Note 2: You can use the provided throwOnInvalidEmailAddress parameter, to decide what to do with invalid addresses e.g. ignore them, throw an exception, ...

Community
  • 1
  • 1
amiry jd
  • 27,021
  • 30
  • 116
  • 215
0

What about to do it like there. And then use it in your constructor:

public class Emails : ObservableCollection<Email>
{
   public Emails()
   {
   }

   public Emails(IEnumerable<Email> emails)
   {
      foreach (var e in emails)
      {
        if(IsValidEmail(e.Address)
        {
          Add(e);
        }
      }
   }

  public IEnumerable<Email> GetAll()
  {
     return Items;
  }    
}
Community
  • 1
  • 1
ivamax9
  • 2,601
  • 24
  • 33