I have a MainEntity
class and it has a collection of SubEntity
. The following is the current validation:
public class MainEntityValidator : AbstractValidator<MainEntity>
{
public MainEntityValidator()
{
RuleFor(x => x.SubEntities).SetCollectionValidator(new SubEntityValidator());
}
public class SubEntityValidator : AbstractValidator<SubEntity>
{
public SubEntityValidator()
{
RuleFor(x => x.Field1).NotNull();
RuleFor(x => x.Field2).NotNull();
}
}
}
How can I add a validation rule so that only unique SubEntity
objects (based on Field1
and Field2
) must be in the collection?
If you need to apply validation rule to collection property but still need access to main model and(or) whole collection, not only item being validated, then RuleForEach
method is your choice:
var comparer = new SubEntityComparer();
RuleForEach(x => x.SubEntities)
.Must((model, submodel) => model.SubEntities.Count(xsub => comparer.Equals(xsub, submodel)) == 1) // one match that ReferenceEquals hit
.WithMessage("The item with values {0}, {1} has duplicates in collection of {2} items",
(model, submodel) => submodel.Field1,
(model, submodel) => submodel.Field2,
(model, submodel) => model.SubEntities.Count); // in validation message generation you can access to current item as well as to main model
If you need only one error message for validation rule you described — you can apply simple predicate rule to collection property SubEntites
:
RuleFor(x => x.SubEntities)
.Must(coll => coll.Distinct(new SubEntityComparer()).Count() == coll.Count)
.WithMessage("One or more items in collection of {0} items are duplicates",
(model, coll) => coll.Count); // has access to collection and to main model
In both cases I used the same equality comparer, but you can override Equals
method as well, and use overloads of IEnumerable
extension methods with overload, that exclude EqualityComparer
parameter.
Code of EqualityComparer listed below:
public class SubEntityComparer : IEqualityComparer<SubEntity>
{
public bool Equals(SubEntity x, SubEntity y)
{
if (x == null ^ y == null)
return false;
if (ReferenceEquals(x, y))
return true;
// your equality comparison logic goes here:
return x.Field1 == y.Field1 &&
x.Field2 == y.Field2;
}
public int GetHashCode(SubEntity obj)
{
return obj.Field1.GetHashCode() + 37 * obj.Field2.GetHashCode();
}
}
Update:
In both ways to implement validation for collection you still can use SetCollectionValidator(new SubEntityValidator())
to validate each item with simple rules independently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With