首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >FluentValidation递归列表导致堆栈溢出

FluentValidation递归列表导致堆栈溢出
EN

Stack Overflow用户
提问于 2019-04-23 12:46:44
回答 3查看 1.8K关注 0票数 1

我使用的是FluentValidation.AspNetcore 8.2.2,并且有一个包含相同类型的子项列表的对象模型。我想使用fluent验证来验证对象。当尝试设置子对象的验证器时,我要么遇到堆栈溢出异常,要么集合发生了变化(典型的foreach循环问题)。

为了测试和找到解决方案,我设置了一个带有单元测试的简单.net核心类库项目。

基本模型

代码语言:javascript
复制
using FluentValidation;

public class BaseModelItem
    {
        public int ItemId { get; set; }

        public string Name { get; set; }

        private List<BaseModelItem> ChildItems { get; set; }
    }

 public class BaseModelItemValidator : AbstractValidator<BaseModelItem>
    {
        public BaseModelItemValidator()
        {
            RuleFor(i => i.ItemId).GreaterThanOrEqualTo(0).WithMessage("Item id may not be negative.");
            RuleFor(i => i.Name).NotNull().NotEmpty().WithMessage("Item name cannot be empty.");
            RuleFor(i => i.ChildItems).ForEach(i => i.SetValidator(new BaseModelItemValidator()));
        }

    }

单元测试

代码语言:javascript
复制
 public class Tests
    {
       [Test]
        public void Test_Name_Cannot_Null()
        {
            var item = new BaseModelItem
            {
                ItemId = 2,
                Name = null,
                ChildItems = new List<BaseModelItem>()
            };
            var validator = new BaseModelItemValidator();
            validator.ShouldHaveValidationErrorFor(t => t.Name, item);
            Assert.Pass();
        }
}

此测试将导致堆栈溢出异常。我尝试过使用后备字段,初始化,甚至更改为数组。通过使用自定义验证器,我可以成功地取消堆栈流异常。

代码语言:javascript
复制
 public class BaseModelItemValidator : AbstractValidator<BaseModelItem>
    {
        public BaseModelItemValidator()
        {
            RuleFor(i => i.ItemId).GreaterThanOrEqualTo(0).WithMessage("Item id may not be negative.");
            RuleFor(i => i.Name).NotNull().NotEmpty().WithMessage("Item name cannot be empty.");
            RuleFor(i => i.ChildItems).Must(BeValidChildItemList);
        }
        private bool BeValidChildItemList(List<BaseModelItem> list)
        {
            if (list.Count > 0)
            {
                RuleFor(i => i.ChildItems).ForEach(i => i.SetValidator(new BaseModelItemValidator()));

            }
            return true;
        }
    }

允许它验证没有子项的对象。但是,如果您使用已填充的子对象运行测试,我会得到错误消息“集合已被修改;枚举操作可能无法执行”。堆栈跟踪

代码语言:javascript
复制
StackTrace:
   at System.ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
   at FluentValidation.AbstractValidator`1.Validate(ValidationContext`1 context) in ****\FluentValidation\src\FluentValidation\AbstractValidator.cs:line 115
   at FluentValidation.DefaultValidatorExtensions.Validate[T](IValidator`1 validator, T instance, IValidatorSelector selector, String ruleSet) in ******\FluentValidation\src\FluentValidation\DefaultValidatorExtensions.cs:line 876
   at FluentValidation.TestHelper.ValidationTestExtension.TestValidate[T,TValue](IValidator`1 validator, Expression`1 expression, T instanceToValidate, TValue value, String ruleSet, Boolean setProperty) in ******\FluentValidation\src\FluentValidation\TestHelper\ValidatorTestExtensions.cs:line 101
   at FluentValidation.TestHelper.ValidationTestExtension.ShouldHaveValidationErrorFor[T,TValue](IValidator`1 validator, Expression`1 expression, T objectToTest, String ruleSet) in *******\FluentValidation\src\FluentValidation\TestHelper\ValidatorTestExtensions.cs:line 40
   at Tests.Tests.Test_Name_Cannot_Null_Nested() in \FluentValidationChildern\FluentValidationChildern.Tests\UnitTest1.cs:line 55

我找不到一个可行的解决方案。

EN

回答 3

Stack Overflow用户

发布于 2019-04-29 06:49:57

虽然我不能流利地使用SetValidator方法来工作,但我确实有一个正在工作并可以改进的变通方法。

在我设置的子列表上,使用“必须”方法,然后实现一个手动函数来循环子项目,手动构造验证器对象并检查结果。

代码语言:javascript
复制
 public class BaseModelItemValidator : AbstractValidator<BaseModelItem>
    {
        public BaseModelItemValidator()
        {
            RuleFor(i => i.ItemId).GreaterThanOrEqualTo(0).WithMessage("Item id may not be negative.");
            RuleFor(i => i.Name).NotNull().NotEmpty().WithMessage("Item name cannot be empty.");
            RuleFor(i => i.ChildItems).Must(BeValidChildItemList);
        }
        private bool BeValidChildItemList(List<BaseModelItem> list)
        {
            if (list == null || list.Count == 0) return true;
            foreach (var child in list)
            {
                var validator = new BaseModelItemValidator();
                var validatorResults = validator.Validate(child);
                if (!validatorResults.IsValid)
                {
                    return false;
                }
            }
            return true;
        }
    }
票数 1
EN

Stack Overflow用户

发布于 2020-06-12 20:56:44

我有一个类似的要求,在中我必须验证一个对象,该对象具有与子对象相同类型的对象列表。我所做的是覆盖了我的验证器的Validate方法,并添加了类似如下的逻辑

代码语言:javascript
复制
public override ValidationResult Validate(ValidationContext<BaseModelItem> context)
{
    var result = base.Validate(context);
    var obj = context.InstanceToValidate;

    if (obj.ChildItems != null && obj.ChildItems.Count > 0)
    {
        foreach (var item in obj.ChildItems)
        {
            var childResult = base.Validate(item);
            foreach (var error in childResult.Errors)
            {
                result.Errors.Add(error);
            }
        }
    }

    return result;
}

不知何故,我很想使用SetValidator,但找不到这样做的方法。但不管怎样,这个重写返回我所期望的ValidationResult对象

票数 0
EN

Stack Overflow用户

发布于 2021-06-11 17:08:32

似乎解决方案是使用相同的验证器实例,如下所示:

代码语言:javascript
复制
public class BaseModelItemValidator : AbstractValidator<BaseModelItem>
{
    public BaseModelItemValidator()
    {
        RuleFor(i => i.ItemId).GreaterThanOrEqualTo(0).WithMessage("Item id may not be negative.");
        RuleFor(i => i.Name).NotNull().NotEmpty().WithMessage("Item name cannot be empty.");
        RuleFor(i => i.ChildItems).ForEach(i => i.SetValidator(this));
    }

}

我可以确认这对我是有效的。归功于此issue

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55804470

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档