Как проверить элементы в словарепросмотреть свойство модели с помощью FluentValidation - PullRequest
2 голосов
/ 18 октября 2019

Я использую структуру данных Dictionary<string, T> для хранения телефонных номеров в модели представления. Пользователь может добавить или удалить их на стороне клиента, прежде чем отправлять их обратно на сервер.


История вопроса: Я использую словарь, потому что использую структуру данных List<T> сASP.NET MVC 5 требует, чтобы имена полей формы содержали последовательные индексы, начинающиеся с нуля, и для JavaScript становится реальной болью добавлять или удалять эти поля на экране без повторного упорядочения значений индекса. Я нашел использование словаря сделало это очень легко. Сейчас я выполняю задачу проверки концепции, чтобы включить внедрение зависимостей, которое позволяет нам использовать наш сеанс NHibernate для запроса базы данных во время проверок, и использовать тот же сеанс, который контроллеры и модели представления используют вместо шаблона «singleton», который FluentValidationиспользуется с MVC 5.

При использовании атрибута [Validator(typeof(T))] над моделями представления сообщения отображаются в полях просто отлично, но экземпляры валидатора являются одиночными в AppDomain, а сеанс NHibernate, используемый валидаторами, не являетсятот же самый, используемый контроллерами. Это приводит к потере синхронизации данных во время проверки данных. Проверки, которые проверяют базу данных, начинают выдавать неожиданные результаты, поскольку NHibernate кэширует на сервере так много данных и фактически имеет 2 отдельных кэша.


Настройка проекта

  • ASP.NET MVC 5
  • .NET Framework 4.5.1 (но мы можем обновить)
  • FluentValidation v8.5.0
  • FluentValidation.Mvc5 v8.5.0
  • FluentValidation.ValidatorAttribute v8.5.0

Просмотр моделей

public class PersonForm
{
    public PhoneFieldsCollection Phones { get; set; }
}

public class PhoneFieldsCollection
{
    public Dictionary<string, PhoneNumberFields> Items { get; set; }
}

public class PhoneNumberFields
{
    [Display(Name="Country Code")]
    [DataType(DataType.PhoneNumber)]
    public string CountryCode { get; set; }

    [Display(Name="Phone Number")]
    [DataType(DataType.PhoneNumber)]
    public string PhoneNumber { get; set; }

    [DataType(DataType.PhoneNumber)]
    public string Extension { get; set; }

    [Display(Name="Type")]
    public string TypeCode { get; set; }
}

Просмотр валидаторов моделей

public class PersonFormValidator : AbstractValidator<PersonForm>
{
    private readonly IPersonRepository repository;

    public PersonFormValidator(IPersonRepository repository)
    {
        // Later on in proof of concept I will need to query the database
        this.repository = repository;

        RuleForEach(model => model.Phones)
            .SetValidator(new PhoneNumberFieldsValidator());
    }
}

public class PhoneNumberFieldsValidator : AbstractValidator<PhoneNumberFields>
{
    public PhoneNumberFieldsValidator()
    {
        RuleFor(model => model.PhoneNumber)
            .NotEmpty();
    }
}

Код контроллера для проверки моделей просмотра:

private bool IsModelStateValid(PersonForm model)
{
    // The `repository` field is an IPersonRepository object from the DI container
    var validator = new PersonFormValidator(repository);
    var results = validator.Validate(model);

    if (results.IsValid)
        return true;

    results.AddToModelState(ModelState, "");

    return false;
}

код шаблона Razor для визуализации страницы

шаблон уровня страницы

@model PersonForm

@Html.EditorFor(model => model.Phones)

шаблон редактора PhoneFieldCollection

@model PhoneFieldsCollection

<fieldset class="form-group form-group-phones">
    <legend class="control-label col-md-3 required">
        Phone Numbers:
    </legend>

    <div class="col-md-9">
        @Html.ValidationMessageFor(model => model, "", new { role = "alert", @class = "alert alert-danger", @for = Html.IdFor(model => model) + "-addButton" })

        <ol class="list-unstyled">
            @foreach (var item in Model.Items)
            {
                if (item.Value.IsClientSideTemplate)
                {
                    <script type="text/html">
                        @Html.EditorFor(model => model.Items[item.Key])
                    </script>
                }
                else
                {
                    @Html.EditorFor(model => model.Items[item.Key])
                }
            }
        </ol>

        <hr />

        <p>
            <button type="button" class="btn btn-default" id="@Html.IdFor(model => model)-addButton"
                    data-dynamiclist-action="add"
                    data-dynamiclist="fieldset.form-group-phones ol">
                <span class="glyphicon glyphicon-plus"></span>
                Add another phone number
            </button>
        </p>
    </div>
</fieldset>

шаблон редактора PhoneNumberFields

@model PhoneNumberFields

@Html.EditorFor(model => model.PhoneNumber)
@Html.ValidationMessageFor(model => model.PhoneNumber)

Обязательное поле сообщения не отображается

Когда я отправляю форму обратно на сервер с пустым полем номера телефона, я получаю сводное сообщение проверки в верхней части страницы, говорящее «Поле номера телефонатребоватьд », чего я и ожидаю. Однако вызов ValidationMessageFor(model => model.PhoneNumber) в шаблоне редактора не приводит к появлению сообщения проверки в поле формы.

При запуске приложения в режиме отладки я получаю Phones[0].PhoneNumber для имени поля, котороеимеет сообщение проверки, но имя поля в модели представления равно Phones.Items[123].PhoneNumber (где 123 - это идентификатор базы данных или отметка времени, сгенерированная new Date().getTime() в JavaScript).

Итак, я знаю почему рядом с полем не появляется сообщение проверки. Задача состоит в том, как я могу это сделать?

Как проверить словарь с помощью FluentValidation, чтобы сообщения об ошибках появлялись в полях формы при использовании **ValidationMessageFor(model => model.PhoneNumber) в шаблоне редактора?


Обновление: Похоже, существует проблема GitHub с 2017 года, связанная с: Поддержка проверки IDictionary . Человек нашел обходной путь, но сопровождающий для FluentValidation в основном сказал, что поддержка - это чудовищная боль и потребует серьезной работы по рефакторингу. Я мог бы попытаться возиться с этим и опубликовать ответ, если смогу заставить что-то работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...