Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda Func<> and Fluent

There are lots of Fluent implementations out there now that work with Lambdas to do things that are quite neat. I'd like to wrap my brain around it so I can start creating some of these things, but I have yet to find an explanation that my brain understands.

Consider this simple example of a Person Validator

public class PersonValidator : IValidator<Person>
{
     public PersonValidator()
     {
          AddRule(p => p.FirstName).CannotBeNull().CannotBeBlank();
          AddRule(p => p.LastName).CannotBeNull().CannotBeBlank();
     }

     public List<ValidationResult> Validate(Person p)
     {
         // pseudo...
         apply all rules specified in constructor, return results
     }
}

I've managed to get part of all of this working using a method on my Validator like this...

public ValidationResult<T,TProp> AddRule<T,TProp>(Func<T,TProp> property)
{
    ... not sure what to do here.  This method gives me the ability to use the lambda
    ... for specifying which properties i want to validate
}

I can then create Extension methods that extend IValidator for the purposes of CannotBeNull and CannotBeEmpty.

So it seems I have the first half and the second half of the problem but I'm not sure how to bring them together.

Looking for a meaningful explanation...I'd like to "Get it". :)

like image 584
ctorx Avatar asked Aug 21 '09 03:08

ctorx


1 Answers

The key to fluent interfaces is that methods like CannotBeNull() and CannotBeBlank() return the current instance (i.e. this). If you want your AddRule method to be "fluent", instead of returning ValidationResult, you need to return the current instance of IValidator. Your extension methods would also need to return the instance of IValidator they are extending.

I think your exact implementation might need to be a bit more complex, and hopefully the example below will provide some insight. Same general rule, however...return "this" to create a fluent interface:

interface IValidator<T>
{
    IValidatorRule<T, TProp> AddRule<TProp>(Func<T, TProp> property);
}

interface IValidatorRule<T>
{
    T instance { get; }
    string PropertyName { get; }

    ValidationResult Apply(T instance);
}

public static IValidatorAugmentorExtensions
{
    public static IValidatorRule<T> CannotBeNull(this IValidatorRule<T> rule)
    {
        // ...

        return rule;
    }

    public static IValidatorRule<T> CannotBeBlank(this IValidatorRule<T> rule)
    {
        // ...

        return rule;
    }
}

The above could be used like so:

public class PersonValidator: IValidator<Person>
{
    public PersonValidator()
    {
        AddRule(p => p.FirstName).CannotBeNull().CannotBeEmpty();
        AddRule(p => p.LastName).CannotBeNull().CannotBeEmpty();
    }    

    public List<ValidationResult> Validate(Person p)
    {
        List<ValidationResult> results = new List<ValidationResult>();

        foreach (IValidatorRule<Person> rule in rules) // don't know where rules is, or what the AddRule method adds to...you'll need to figure that out
        {
            results = rule.Apply(p);
        }

        return results;
    }
}

While the above demonstrates how to create a fluent interface, I don't really know what it buys you in the long run in this particular situation. For the convenience of a fluent interface that seems to be used only internally to concrete validators, you have increased the complexity of your code by a fair amount, without really providing a useful, fluent interface to the consumers of your validators. I would think you would glean more value by providing a fluent validation framework to the developers who need to be performing validation, rather than providing a fluent framework for creating concrete validators.

like image 96
jrista Avatar answered Oct 12 '22 09:10

jrista