Validate that One of Two Properties is Provided

Sometimes, you want to make sure that one of two properties is provided by a user. It's not natively supported in Asp.Net Core so here's a custom attribute to help with that.

ebeeraheem

View Profile
22 views
Jun 17, 2025
Updated Jun 18, 2025
/// <summary>
/// Validation attribute that ensures at least one of two specified properties has a value.
/// Both properties can have values, but at least one must be provided.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class RequireOneOfTwoAttribute : ValidationAttribute
{
   public string FirstProperty { get; }
   public string SecondProperty { get; }
   public string FirstDisplayName { get; set; }
   public string SecondDisplayName { get; set; }
   public RequireOneOfTwoAttribute(string firstProperty, string secondProperty)
   {
       FirstProperty = firstProperty ?? throw new ArgumentNullException(nameof(firstProperty));
       SecondProperty = secondProperty ?? throw new ArgumentNullException(nameof(secondProperty));
       
       // Set default display names
       FirstDisplayName = firstProperty;
       SecondDisplayName = secondProperty;
   }
   protected override ValidationResult IsValid(object value, ValidationContext validationContext)
   {
       if (value == null)
       {
           return new ValidationResult($"Either {FirstDisplayName} or {SecondDisplayName} must be provided.");
       }
       var firstProp = validationContext.ObjectType.GetProperty(FirstProperty);
       var secondProp = validationContext.ObjectType.GetProperty(SecondProperty);
       if (firstProp == null)
       {
           throw new ArgumentException($"Property '{FirstProperty}' not found on type '{validationContext.ObjectType.Name}'");
       }
       if (secondProp == null)
       {
           throw new ArgumentException($"Property '{SecondProperty}' not found on type '{validationContext.ObjectType.Name}'");
       }
       var firstValue = firstProp.GetValue(value);
       var secondValue = secondProp.GetValue(value);
       // Check if both values are null or empty
       bool firstIsEmpty = IsNullOrEmpty(firstValue);
       bool secondIsEmpty = IsNullOrEmpty(secondValue);
       if (firstIsEmpty && secondIsEmpty)
       {
           return new ValidationResult(
               $"Either {FirstDisplayName} or {SecondDisplayName} must be provided.",
               new[] { FirstProperty, SecondProperty });
       }
       return ValidationResult.Success;
   }
   private static bool IsNullOrEmpty(object value)
   {
       return value switch
       {
           null => true,
           string str => string.IsNullOrWhiteSpace(str),
           _ => false
       };
   }
}

Here's an example of how you can use this attribute. Imagine you have a signup model and you require a user to provide either an email address or a phone number. It's okay if they provide both, but they must provide at least one of the two properties.

[RequireOneOfTwo(nameof(Email), nameof(PhoneNumber),
    FirstDisplayName = "Email Address",
    SecondDisplayName = "Phone Number")]
public class SignupModel
{
   public string Email { get; set; } = string.Empty;   
   public string PhoneNumber { get; set; } = string.Empty;
   public required string FirstName { get; set; }
   public required string LastName { get; set; }
}

FirstDisplayName and SecondDisplayName are optional properties to improve readability.