FluentValidation Рекурсивный список вызывает переполнение стека - PullRequest
1 голос
/ 23 апреля 2019

Я использую FluentValidation.AspNetcore 8.2.2 и имею объектную модель, которая содержит список дочерних элементов того же типа. Я хотел бы использовать свободное подтверждение для проверки объекта. При попытке установить валидатор для дочернего объекта я либо сталкиваюсь с исключением переполнения стека, и / или коллекция изменилась (типичная проблема цикла foreach).

Чтобы протестировать и найти разрешение, я настроил простой проект библиотеки классов ядра .net с модульным тестом.

Базовая модель

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

    }

модульный тест

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

Этот тест вызовет исключение переполнения стека. Я пытался использовать резервные поля, инициализации или даже изменения в массив. Я могу успешно отменить исключение потока стека, используя специальный валидатор.

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

Позволяет проверять объекты без дочерних элементов. Однако, если вы запускаете тест с заполненными дочерними объектами, я получаю сообщение об ошибке «Коллекция была изменена; операция перечисления может не выполняться». Трассировка стека

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

Я не могу найти подходящее решение.

1 Ответ

0 голосов
/ 29 апреля 2019

Хотя мне не удалось заставить Fluent работать с помощью метода SetValidator, у меня есть обходной путь, который работает и может быть улучшен.

в дочернем списке, который я установил, использую метод 'Must', а затем реализую ручную функцию для зацикливания дочерних элементов и создания объекта валидатора вручную и проверки результата.

 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;
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...