Search code examples
nrules

How to create a validation rule with NRules?


I just discovered NRules for .NET. I would like to create a rule that prevents object construction if a field/property value is invalid. For instance, given the following class:

public class Customer
{
    public string Name { get; private set; }
    public bool IsPreferred { get; set; }

    public Customer(string name)
    {
        Name = name;
    }
}

I would like a rule created that would throw an exception when 'Name' is null or empty, thus, cancelling object creation, as well as generate a user defined message: Customer Name must be specified.

Can this be done with NRules, if so, how is it done?


Solution

  • With NRules you write rules in terms of a particular domain or object model. So you need some object to hold the data, so that you can then insert those objects into the rules session, and can also match those objects with the rules. I would imagine for a validation scenario you would use one of the following:

    • DTO from the service layer
    • ViewModel object from the presentation layer
    • Builder object before calling its Build method (i.e. a CustomerBuilder)
    • Domain object itself (constructing it, then validating and discarding if not valid)

    At a higher level, the sweet spot for NRules is expressing volatile business logic in terms of a stable domain model. In this case you can separate validation logic into two groups - 1) validation that's stable and defines intrinsic invariant for the domain objects, i.e. customer name is not blank; and 2) volatile validation logic (i.e. customer is preferred if certain conditions are met). You would then encode validation logic of type 1 as assertions in the domain model itself, and validation logic of type 2 as rules in terms of those domain objects.

    Either way you go, at the mechanical level, you probably want validation rules to insert a ValidationError for every failed validation, and then at the end query session for these validation errors and prevent object construction if there are any.

    ObjectUnderValidation match = null;
    
    When()
      .Match<ObjectUnderValidation>(() => match, x => x.ValidationCondition);
    
    Then()
      .Do(ctx => ctx.Insert(new ValidationError(match, "Message")));
    

    Later when doing validation:

    var session = factory.CreateSession();
    session.Insert(myObjectUnderValidation);
    
    session.Fire();
    var errors = session.Query<ValidationError>().ToList();
    
    if (errors.Any())
      //Don't construct
    else
      //Construct
    

    You can also abstract some of the boiler-plate code here to make it easier to use in your specific scenario.