15

I have a a couple of variables that i define in C# by:

public String firstName { get; set; }
public String lastName { get; set; }
public String organization { get; set; }

What i want is to add validation to these methods when you try to set a value. Lets say your going to set a value for firstName, the i should pass thrue a regexp to actuelly be set, otherwise an exception should be thrown. Is this possible to build with this "short syntax" or should I go for standard (like in JAVA) getters and setters and in there validate the data?

Marthin
  • 6,413
  • 15
  • 58
  • 95

5 Answers5

30

If you want to validate when the property is set, you need to use non-auto properties (i.e., manually defined get and set methods).

But another way to validate is to have the validation logic separate from the domain object.

class Customer {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Organization { get; set; }
}

interface IValidator<T> {
    bool Validate(T t);
}

class CustomerValidator : IValidator<Customer> {
    public bool Validate(Customer t) {
        // validation logic
    }
}

Then, you could say:

Customer customer = // populate customer
var validator = new CustomerValidator();
if(!validator.Validate(customer)) {
    // head splode
}

This is the approach I prefer:

  1. A Customer should not responsible for validating its own data, that is another responsibility and therefore should live elsewhere.
  2. Different situations call for different validation logic for the same domain object.
Michael Haddad
  • 4,085
  • 7
  • 42
  • 82
jason
  • 236,483
  • 35
  • 423
  • 525
  • 8
    I don't agree, even if your validation fails, your customer data is invalid. What's to stop it from writing garbage to the database if you mess up external validation? – Blindy May 25 '11 at 15:58
  • 2
    @Blindy: Your persistence layer should be wired up with validators that don't persist if the validation fails. – jason May 25 '11 at 15:59
  • 3
    @Blindy Customer shouldn't be responsible for writing to the database. That would be another responsibility that Customer shouldn't have. – Phil May 25 '11 at 16:02
  • I understand the "Single Responsibility Principle" (SRP) but I always wonder where the 'cut-off' should be. Meaning, you could take it too far...like creating a class to set properties (for you). I'm not saying anything about your example...I just seem to find myself asking 'How far should I go in applying SRP'? ...maybe I should ask this as a question? – Prisoner ZERO May 26 '11 at 12:34
  • @Prisoner ZERO: I think that's the good basis for another question, but you'll need to come up with a specific example to make it better defined. – StriplingWarrior May 26 '11 at 14:55
  • A little late but there are 2 kinds of validations IMO. One is related to the workings of the class' methods; If the methods require certain conditions for properties then it makes sense to validate those within the class. If you are validation conditions related to how the class is used for some implementation, then that should be separate from the class because of the Open Closed principle; the implementation details may change so you want to separate that from your class. – Timothy Jannace Apr 18 '19 at 17:41
  • Another problem with the separate validator approach is that you can never be sure that you have a validated instance of the Customer class when you see it in the code. If you find that you are having a lot of `NullReferenceException` problems, then it is very likely you are suffering from this. – Daniel Lidström Mar 30 '20 at 11:07
  • What is the value of the interface here? I think it complicates things. – NoChance Oct 16 '20 at 21:57
  • Now everytime I want to know my object is valid I have to call the `validate` method? That puts the responsibility on all users of the object. But I guess in C# land this is the way to go... – wilmol Mar 03 '21 at 03:40
  • I also disagree with @jason and agree with Blindy. You can end up with an invalid aggregate if you choose to delegate validation of a property when it is set, to another class. Aggregates should be responsible for their own consistency (ie. maintaining invariants). – Paul-Sebastian Manole Apr 17 '21 at 12:16
15

What you have now are called "auto-properties," and only perform a simple "get/set". In order to customize the behavior of the get or set, you will need to convert the properties to field-backed properties:

private string _firstName;
public string FirstName 
{ 
    get {return _firstName;} 
    set 
    {
       Validate(value); _firstName = value;
    }
}

Note that I changed String to string and capitalized the property name, in following accepted C# naming best practices.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
2

It's best practice to apply SRP. Set the validation in a separate class.

You can use FluentValidation

       Install-Package FluentValidation

You would define a set of validation rules for Customer class by inheriting from AbstractValidator<Customer>:

Example:

  public class CustomerValidator : AbstractValidator<Customer> {
    public CustomerValidator() {
      RuleFor(x => x.Surname).NotEmpty();
      RuleFor(x => x.Forename).NotEmpty().WithMessage("Please specify a first name");
      RuleFor(x => x.Discount).NotEqual(0).When(x => x.HasDiscount);
      RuleFor(x => x.Address).Length(20, 250);
      RuleFor(x => x.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
    }

    private bool BeAValidPostcode(string postcode) {
      // custom postcode validating logic goes here
    }
  }

To run the validator, instantiate the validator object and call the Validate method, passing in the object to validate.

   Customer customer = new Customer();
   CustomerValidator validator = new CustomerValidator();

   ValidationResult result = validator.Validate(customer);

 if(! results.IsValid) {
   foreach(var failure in results.Errors) {
       Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage);

} }

M.Hassan
  • 10,282
  • 5
  • 65
  • 84
2

I wouldn't add validation in the setter at all. Rather, I would create a function called validate instead...that way all your validation code is in one spot rather scattered throughout your setters.

Prisoner ZERO
  • 13,848
  • 21
  • 92
  • 137
0

You have to use the full property syntax for this.

Blindy
  • 65,249
  • 10
  • 91
  • 131