FluentValidation检查子集合中的重复实体

时间:2015-05-19 18:31:15

标签: c# fluentvalidation

我有一个MainEntity类,它有一个SubEntity的集合。以下是当前的验证:

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

如何添加验证规则,以便只有唯一的SubEntity个对象(基于Field1Field2)必须位于集合中?

2 个答案:

答案 0 :(得分:3)

如果您需要将验证规则应用于集合属性,但仍需要访问主模型和(或)整个集合,不仅要验证项目,那么RuleForEach方法是您的选择:

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

如果您只需要一条错误消息用于您所描述的验证规则 - 您可以将简单谓词规则应用于集合属性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

在这两种情况下,我都使用相同的相等比较器,但您也可以覆盖Equals方法,并使用带有重载的IEnumerable扩展方法的重载,排除EqualityComparer参数。

下面列出的EqualityComparer代码:

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

更新:

在实现收集验证的两种方式中,您仍然可以使用SetCollectionValidator(new SubEntityValidator())独立地使用简单规则验证每个项目。

答案 1 :(得分:0)

我想您的问题的答案是将您的集合中的每个项目与其他项目进行比较,并且在对象“相等”时无法验证。虽然如果你的收藏品可能包含的物品数量不仅仅是一小部分,我也不建议这样做。更好的方法是实现Equals()和GetHashCode(),以便基类库中的集合类型可以预测地处理您的SubEntity对象。我会发布一个例子,但我不知道你的对象是什么样的,因为你没有发布它们。无论如何,如果以有意义的方式实现这些方法,则可以将集合定义为已具有适当唯一约束的类型(例如,HashSet of T)。