Water Cooler v2
Water Cooler v2

Reputation: 33850

One of two properties is required from the data model

Let's say I have a Person class with FirstName and LastName. I want that the user must enter at least one of the two values in the UI but he may not have to enter each of them.

If I place the Required attribute / data annotation on each of them, that makes both of them required.

How do I make a server side validation (with client side validation, too) for this rule?

Upvotes: 11

Views: 4371

Answers (2)

tar
tar

Reputation: 326

You need validation on both sides: frontend and backend.

Frontend validation should ensure that inputs are correct in order to give the user direct feedback before doing another roundtrip to the server. Here, you need to prevent legit but wrong strings:

  • null (as string is a reference type)
  • "" (empty)
  • " " (whitespace)
  • "." (is not a name)
  • "4" (is not a name)
  • " Hans" (is a wrongly entered name as it has an unneccessary leading space)
  • "hans" (is a wrongly entered name as the first char should be uppercase)
  • etc.

On the other hand, "Asdf" is not a name either while "X Æ A-Xii" is a legit name (ask Elon Musk).

Backend validation should then handle concerns of security.

In case you want to only allow instanciation with depending properties, you need to implement the corresponding constructors. If you would end with the same parameter types (here, FirstName and LastName are both type string, so different constructors would not be possible) you provide a constructor with an additional parameter that decides where the input parameter should be assigned to. Furthermore, you only allow private set accessors and provide additional setter methods, if necessary:

public class Person {
  public string FirstName { get; private set; } = string.Empty;
  public string LastName { get; private set; } = string.Empty;

  public Person(string firstName, string lastName) {
    FirstName = firstName;
    LastName = lastName;
  }

  public Person(string name, bool nameIsFirstName = false) {
    if (nameIsFirstName) {
      FirstName = name;
    } else {
      LastName = name;
    }
  }

  public void UpdateNames(string firstName, string lastName) {
    FirstName = firstName;
    LastName = lastName;
  }

  public void UpdateName(string name, bool nameIsFirstName = false) {
    if (nameIsFirstName) {
      FirstName = name;
    } else {
      LastName = name;
    }
  }
}

Furthermore, you should also prevent duplicates:

  • frontend: use a request/payload buffer to prevent accidental double calls to the server
  • backend: check unique values against the existing repository
  • repository (e.g. database): set unique constraints

Upvotes: 0

Ulises
Ulises

Reputation: 13419

You could use a custom attribute for this. In short, the custom attribute will retrieve both values and then ensure at least one has a value. See this page for more information. Here is an example (untested code):

[AttributeUsage(AttributeTargets.Property, AllowMultiple =false, Inherited = false)]
  public class ValidatePersonName: ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      string FirstName = (string)validationContext.ObjectType.GetProperty("FirstName").GetValue(validationContext.ObjectInstance, null);

      string LastName = (string)validationContext.ObjectType.GetProperty("LastName").GetValue(validationContext.ObjectInstance, null);

  //check at least one has a value
  if (string.IsNullOrEmpty(FirstName) && string.IsNullOrEmpty(LastName))
        return new ValidationResult("At least one is required!!");

      return ValidationResult.Success;
    }
  }

Usage:

class Person{

 [ValidatePersonName]
 FirstName{get;set;}

 LastName{get;set;}
}

Upvotes: 18

Related Questions