Guides
Last updated
July 25, 2025

5 Ways to Implement Email Validation in C# Data Annotations

Nicolas Rios
Nicolas Rios
Table of Contents:
ON THIS PAGE
Get your free
email validation
 API key now
stars rating
4.8 from 1,863 votes
See why the best developers build on Abstract
START FOR FREE
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
No credit card required

Validating emails is a common task when using C# data annotations to maintain data quality. Here, we explore five distinct methods for implementation, providing working code for each. You will see the pitfalls of common techniques and how Abstract API offers a more dependable alternative.

How to Implement Email Validation in Data Annotations

Here are four ways to add email validation to your data models. Each method uses a different C# feature, from built-in attributes to custom validation logic.

Built-in EmailAddressAttribute

You can annotate a property with the [EmailAddress] attribute. At runtime, the validation pipeline calls the EmailAddressAttribute.IsValid method. The behavior of this method depends on the .NET framework version.

In .NET 4.x, it relies on a regular expression inspired by RFC-5322. For .NET 5 and newer versions, the regex was removed, and the check became much looser. It essentially passes any string that contains an "@" symbol. Empty strings are considered invalid unless you also add the [Required] attribute, as there is no AllowEmpty flag.

public class UserDto
{
    [EmailAddress(ErrorMessage = "Bad e-mail")]
    public string? Email { get; set; }
}

RegularExpressionAttribute with a Custom Pattern

This method involves the [RegularExpression] attribute. You attach it to a property and provide a custom pattern that you control. This is a classic way to restore the strictness of pre-.NET 5 frameworks or to enforce specific corporate constraints, such as a "company.com only" rule. The same pattern works on both the server and the client.

private const string EmailPattern =
    @"^[A-Za-z0-9._%+-]+@([A-Za-z0-9-]+\.)+[A-Za-z]{2,24}$";

public class Employee
{
    [RegularExpression(EmailPattern, ErrorMessage = "Corp e-mail only")]
    public string Email { get; set; } = "";
}

Custom ValidationAttribute with System.Net.Mail.MailAddress

You can create a custom validation attribute. This requires you to subclass ValidationAttribute and delegate the validation logic to new MailAddress(email). This constructor tokenizes the email address and normalizes the domain with punycode. This approach catches obvious syntax errors without the need to maintain a complex regular expression. The attribute can be reused across different models.

public sealed class StrictEmailAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(
        object? value, ValidationContext ctx)
    {
        if (value is not string s || string.IsNullOrWhiteSpace(s))
            return ValidationResult.Success;

        try
        {
            var addr = new System.Net.Mail.MailAddress(s);
            // optional: reject addresses lacking MX
        }
        catch
        {
            return new ValidationResult(ErrorMessage ??
                $"{ctx.MemberName} is not a valid e-mail.");
        }
        return ValidationResult.Success;
    }
}

Model-Level Validation with IValidatableObject

This technique requires your DTO to implement the IValidatableObject interface. You then add your logic inside the Validate(IEnumerable<ValidationResult>) method. Inside this method, you have access to the full object graph.

This allows for the implementation of complex, cross-field rules. For example, you can check if an email address uses a corporate domain when a IsBusinessContact flag is true. This approach keeps validation attribute noise off the model properties.

public class Contact : IValidatableObject
{
    public string? Email { get; set; }
    public bool IsBusinessContact { get; set; }

    public IEnumerable<ValidationResult> Validate(
        ValidationContext ctx)
    {
        if (string.IsNullOrWhiteSpace(Email))
            yield break;

        if (IsBusinessContact &&
            !Email.EndsWith("@contoso.com",
                StringComparison.OrdinalIgnoreCase))
            yield return new(
                "Business contacts must use a contoso address",
                new[] { nameof(Email) });

        // Delegate to central StrictEmailAttribute or a MX checker
    }
}

Challenges of Implementing Email Validation in Data Annotations

These traditional methods present several validation hurdles. The reliance on regular expressions and inconsistent framework behavior often leads to unreliable outcomes and potential security vulnerabilities for your application.

  • RFC compliance is brutally complex. The EmailAddressAttribute uses a simple regex that rejects valid international emails but accepts some malformed inputs, which creates an unreliable filter.
  • A spec-valid email does not guarantee deliverability. Mailbox providers have their own rules that differ from the official RFC standard. Syntax checks in data annotations often fail to predict if an email address actually works.
  • The behavior of EmailAddressAttribute changes between .NET versions. Older frameworks use a strict regex, while newer ones use a looser check. Identical code can produce different validation results depending on the deployment environment.
  • Complex patterns with RegularExpressionAttribute risk catastrophic backtracking, a ReDoS vulnerability. Teams must choose between a fast but unsafe regex or a slow, overly strict one, a difficult trade-off between performance and accuracy.

Validate Emails with Abstract API
Ensure data integrity in your C# project by implementing robust email validation with Data Annotations.
Get started for free

How Abstract API Handles Email Validation in C# Data Annotations

Abstract API addresses the core weaknesses of traditional methods with a multi-layer remote check that verifies email deliverability in real time.

  • The standard EmailAddressAttribute in C# now performs little more than a null or empty test. This change means syntactically incorrect or undeliverable addresses often pass validation.
  • Traditional attributes cannot see MX records, check against disposable-domain lists, or interpret SMTP responses. This limitation results in "valid" addresses that bounce or pollute analytics.
  • The API inserts a remote check that covers syntax, typos, role accounts, free providers, and disposable domains. It also validates MX records and SMTP responses to return a single deliverability verdict.
  • You can wrap the API call in a custom ValidationAttribute. This action preserves the declarative model style and avoids changes to controller code or front-end scripts.
  • The service receives independent updates, so you automatically gain new filters and improved heuristics without a need to redeploy your own application.

How to Bring Abstract API to Your Dev Environment

Once you know Abstract's capabilities, you add its email validation API to your project with ease.

  • Sign up at Abstract and get your API key from the dashboard.
  • Add the System.Net.Http.Json or Newtonsoft.Json NuGet package to your project.
  • Store the API key in your appsettings.json file: "Abstract": { "ApiKey": "YOUR_KEY" }.

Next, create a custom validation attribute that calls the API, then annotate your model. This approach keeps your model definitions clean and declarative.

Sample Email Validation Implementation with Abstract API

The API returns a detailed JSON object. The primary field is deliverability, which offers a clear verdict to accept or reject an email. The quality_score quantifies risk, while autocorrect suggests a fix for typos. Other boolean fields let you enforce custom policies, like the rejection of disposable email addresses. Here is a sample response for a valid email:

Final Thoughts

Traditional validation attributes perform superficial checks that fail to catch undeliverable emails. Abstract API replaces these weak tests with a real-time, multi-layer verification to ensure you only accept valid emails from legitimate domains. For reliable validation, consider an account on Abstract API and get your free API key.

Frequently Asked Questions

What does the built-in C# EmailAddressAttribute actually check?

In .NET 5 and newer, the built-in [EmailAddress] attribute performs a very loose check, essentially passing any string that contains an "@" symbol that is not at the very start or end. In older .NET 4.x frameworks it used a stricter RFC-5322-inspired regex. Because the behavior differs across framework versions, identical model code can produce different validation results depending on where it is deployed.

When should I use a RegularExpressionAttribute instead of EmailAddressAttribute for email validation in C#?

Use [RegularExpression] when you need to restore the strictness that [EmailAddress] lost in .NET 5+, or when you need business-specific constraints such as allowing only a single corporate domain. The trade-off is that complex regex patterns can introduce catastrophic backtracking (ReDoS) vulnerabilities, so the chosen pattern must be carefully reviewed for performance and safety.

How can I validate an email address using System.Net.Mail.MailAddress in a custom ValidationAttribute?

You create a class that inherits from ValidationAttribute and, inside IsValid, attempt to instantiate a MailAddress object with the input string. If the constructor throws, the value is invalid. This approach delegates parsing logic to the .NET framework itself (including punycode encoding for international domains) without requiring you to write or maintain a regex.

What is IValidatableObject and when does it help with email validation?

Implementing IValidatableObject on a model class gives the Validate method access to the full object graph, not just a single property. This is useful when email validity depends on other fields: for example, requiring a specific email domain when a user selects a "business account" type. Attribute-level validators cannot read sibling properties, so IValidatableObject is the right pattern for those cross-field rules.

Why is syntax validation alone not enough for email validation in C#?

A syntactically correct address can still be undeliverable: the domain may not exist, the MX records may be missing, the mailbox may not exist, or the address may belong to a disposable or role-based service. Local data annotations have no way to check any of these conditions because they work entirely offline against the string value. Real-time API-based validation is required to confirm actual deliverability.

How does Abstract improve on built-in C# email validation?

Abstract adds a remote validation layer that checks syntax, common typos, role accounts (e.g., info@, noreply@), disposable domain detection, MX record lookup, and SMTP-level verification in a single request. This covers the deliverability gaps that attribute-based validation cannot address, and it can be integrated into ASP.NET models alongside standard data annotations to provide both client-side format checks and server-side deliverability confirmation.

Validate Emails with Abstract API
Don't let bad data in. Implement proper email validation in C# using data annotations.
Get started for free

Related Articles

Phone Validation
key now
Get your free
stars rating
4.8 from 1,863 votes
See why the best developers build on Abstract
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
No credit card required