Reputation: 2159
This is the first time I'm trying to implement FluentValidation since I need to cover a complex validation scenario.
The class I'm trying to validate has a large quantity of properties, complex objects and several collections.
I didn't have troubles to validate properties of the main class or even checking if collections are not empty, but I do have problems while validating objects properties within each collection.
To implement this I followed the examples documented here (check under "Re-using Validators for Collections"): http://fluentvalidation.codeplex.com/wikipage?title=creatingavalidator
These are my model classes (reduced to improve readability)
public class Caso
{
public int Id { get; set; }
public string Descripcion { get; set; }
public List<Medicamento> Medicamentos { get; set; }
}
public class Medicamento
{
public int Id { get; set; }
public string Nombre { get; set; }
}
These are the validator classes:
public class CasoValidator : AbstractValidator<CasoAdverso>
{
public CasoValidator()
{
RuleSet("Iniciar", () =>
{
// Validated OK
RuleFor(x => x.Descripcion).NotEmpty();
// Validated OK
RuleFor(x => x.Medicamentos).Must(x => x != null && x.Count > 0).WithMessage("No puede iniciar un caso sin medicamentos cargados");
RuleFor(x => x.Medicamentos).SetCollectionValidator(new MedicamentoValidator());
});
}
}
public class MedicamentoValidator : AbstractValidator<Medicamento>
{
public MedicamentoValidator()
{
// NOT Validated. Even if the object property is empty the error message doesn't appear. I also checked using "NotNull" and "NotEmpty" clauses
RuleFor(x => x.Nombre).NotNull().WithMessage("Debe especificar un nombre");
}
}
(Note: I'm using RuleSet because of different validation schemas which are dependent of the document status in the workflow)
I'm executing the validation manually from the controller (no MVC integration)
[HttpPost]
public ActionResult Iniciar(Caso c)
{
CasoValidator validator = new CasoValidator();
FluentValidation.Results.ValidationResult validate = validator.Validate(c, ruleSet: "Iniciar");
// ...
}
With this implementation properties of the main class are validated fine but I need also to validate each property of the "Medicamento" class within the collection.
Could I be missing something here?. Should this be validated using the RuleForEach clause available?
Any help will be appreciated.
Upvotes: 3
Views: 4074
Reputation: 205
as a complementary: a collection named GroupMemberIds should have AdminMemebrId:
RuleFor(r => new { r.GroupMemberIds, r.AdminMemberId }).Must(a => a.GroupMemberIds.Contains(a.AdminMemberId));
Upvotes: 0
Reputation: 2579
It appears the RuleSet setting is applying to the child validator as well as the primary one.
I tested your code in an xUnit.net test, and confirmed it.
If you change your rulesets to execute you should find it works as expected:
CasoValidator validator = new CasoValidator();
FluentValidation.Results.ValidationResult validate = validator.Validate(c, ruleSet: "default,Iniciar");
The 'default' ruleset will work on the MedicamentoValidator rules.
I didn't find this in the documentation, only through testing.
This is the sample unit test:
[Fact]
public void Test1()
{
Caso c = new Caso()
{
Id = 1,
Descripcion = "none",
Medicamentos = new List<Medicamento>()
};
c.Medicamentos.Add(new Medicamento()
{
Id = 0,
Nombre= null
});
CasoValidator validator = new CasoValidator();
FluentValidation.Results.ValidationResult validate = validator.Validate(c, ruleSet: "default,Iniciar");
Assert.NotEmpty(validate.Errors);
}
Update: i found a reference by Jeremy Skinner for exactly this behavior: http://fluentvalidation.codeplex.com/discussions/266920
Rulesets cascade to any child validators, so whichever ruleset is selected for use by the top-level validator will also be used by the child validator. So if you ran the "Minimal" ruleset on the CreateProfileModelValidator, then only rules in the "Minimal" ruleset will be run on both the CreateProfileModelValidator and the ProfileValidator.
Upvotes: 1