FluentValidation checking for duplicate entity in a sub-collection

空扰寡人 提交于 2019-12-10 09:28:52

问题


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?


回答1:


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.




回答2:


I suppose the answer to your question is to compare every item in your collection to every other item and fail validation when to objects are "equal". Though I would not recommend doing that if your collection could contain more than a trivial number of items. A better approach would be to implement Equals() and GetHashCode() so that the collection types in the base class library will handle your SubEntity objects predictably. I would post an example but I don't know what your objects look like as you have not posted them. At any rate, if you implement those methods in a meaningful way you can define your collection as a type that already has the appropriate unique constraints (HashSet of T for instance).



来源:https://stackoverflow.com/questions/30333421/fluentvalidation-checking-for-duplicate-entity-in-a-sub-collection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!