2009-08-21 9 views
7

Existen muchas implementaciones Fluent que funcionan con Lambdas para hacer cosas que son bastante ordenadas. Me gustaría adaptar mi cerebro para que pueda comenzar a crear algunas de estas cosas, pero aún no he encontrado una explicación que mi cerebro entienda.Lambda Func <> y Fluent

consideran este ejemplo simple de una persona Validador

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 
    } 
} 

he logrado conseguir parte de todo esto de trabajo utilizando un método en mi Validador así ...

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 puede crear métodos de extensión que extienden IValidator para los propósitos de CannotBeNull y CannotBeEmpty.

Parece que tengo la primera mitad y la segunda mitad del problema, pero no estoy seguro de cómo juntarlos.

Buscando una explicación significativa ... Me gustaría "obtenerlo". :)

+0

tu ejemplo no tiene sentido, cuando lo hace AddRule(). CannotBeNull(). CannotBeBlank() está diciendo que desea agregar esas reglas a las reglas de validación y aplicarlas más adelante? –

+0

Sí, precisamente. Deseo poder usar AddRule seguido de cualquier cantidad de métodos encadenados que apliquen la validación en una propiedad dada de la clase. Mi desafío es que no sé qué hacer dentro de "AddRule". Sé que necesito persistir en el validador pero no sé cómo hacer eso. – ctorx

Respuesta

5

La clave para las interfaces fluidas es que los métodos como CannotBeNull() y CannotBeBlank() devuelven la instancia actual (es decir, esto). Si desea que su método AddRule sea "fluido", en lugar de devolver ValidationResult, debe devolver la instancia actual de IValidator. Sus métodos de extensión también necesitarían devolver la instancia de IValidator que están extendiendo.

Creo que su implementación exacta podría necesitar ser un poco más compleja, y espero que el ejemplo a continuación brinde alguna información. regla general mismo, sin embargo ... volver "este" para crear una interfaz fluida:

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; 
    } 
} 

Lo anterior podría ser utilizado de esta manera:

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; 
    } 
} 

Si bien lo anterior demuestra cómo crear una interfaz fluida , Realmente no sé lo que te compra a largo plazo en esta situación particular. Para la comodidad de una interfaz fluida que parece usarse solo internamente para los validadores concretos, ha aumentado la complejidad de su código en una cantidad justa, sin proporcionar realmente una interfaz útil y fluida para los consumidores de sus validadores. Creo que obtendrías más valor al proporcionar un marco de validación fluido a los desarrolladores que necesitan realizar la validación, en lugar de proporcionar un marco fluido para crear validadores concretos.

+0

+1 eso es lo que iba a escribir, pero me ganaste. Así que tomé un enfoque diferente. –

+0

Necesitan clave aquí, es lo que sucede con "Reglas". ¿Cómo se ve la lista local y cómo se utiliza? – ctorx

+0

La solución más simple sería tener las Reglas como una lista local >.El método AddRule debe crear IValidatorRule y agregarlo a esa colección, y los métodos de extensión se pueden usar para modificar esa instancia a través de la interfaz fluida. Dejando eso de lado, necesito enfatizar nuevamente que creo que estás gastando MUCHO esfuerzo por muy poco beneficio. Si realmente quiere darse cuenta de los beneficios de una interfaz fluida, reconsideraría su marco de validación. En lugar de proporcionar validadores concretos (es decir, PersonValidator), proporcione una interfaz fluida para quienes realizan la validación. – jrista

1

La respuesta de jrista es correcta. Solo por un enfoque diferente aquí es cómo lo logré.

public class PersonValidator : IValidator<Person> 
    { 
     List<Func<Person,bool>> validationRules = new List<Func<Person,bool>>(); 

    public PersonValidator() 
    { 
     AddRule(p => IsNullOrEmpty(p.FirstName)).AddRule(p1 => CheckLength(p1.FirstName)); 
    } 

    PersonValidator AddRule(Func<Person,bool> rule) 
    { 
     this.validationRules.Add(rule); 
     return this; 
    } 

    private bool IsNullOrEmpty(String stringToCheck) 
    { 
     return String.IsNullOrEmpty(stringToCheck); 
    } 

    private bool CheckLength(String stringToCheck) 
    { 
     return (String.IsNullOrEmpty(stringToCheck) ? false : stringToCheck.Length < 3); 
    } 

    #region IValidator<Person> Members 

    public bool Validate(Person obj) 
    { 
     return validationRules.Select(x => x(obj)).All(result => result == false); 
    } 

    #endregion 
} 



     Person test = new Person() { FirstName = null }; 
     Person test1 = new Person() { FirstName = "St" }; 
     Person valid = new Person() { FirstName = "John" }; 

     PersonValidator validator = new PersonValidator(); 
     Console.WriteLine("{0} {1} {2}", validator.Validate(test), validator.Validate(test1), validator.Validate(valid)); 
+0

No veo cómo este ejemplo facilitaría este uso ... AddRule (x => x.FirstName) .IsNullOrEmpty(); – ctorx

+1

no sería porque es un enfoque diferente, solo quería terminar mi código en lugar de solo olvidarlo porque alguien más respondió antes que yo. –

Cuestiones relacionadas