Это правильный способ использовать ModelState.Remove для работы с ModelState? - PullRequest
18 голосов
/ 27 июля 2011

Я работаю над большим веб-приложением MVC3 и раздражаюсь по поводу метода ModelState.IsValid.

ModelState используется почти во всех моих контроллерах, поэтому для проверки публикуемых данных. Все представления основаны на ViewModel, которые содержат разные классы, и эти классы, очевидно, содержат свойства, которые могут быть помечены как [Required].

Проблема, с которой я сталкиваюсь, заключается в том, что требуемые свойства иногда не требуются, и мне приходится использовать метод ModelState.Remove, чтобы ModelState.IsValid становилось истинным.

Мой вопрос заключается в использовании ModelState.Remove, это правильный способ ведения дел или есть более эффективный подход.

Ответы [ 5 ]

20 голосов
/ 23 декабря 2012

Вот мое решение - метод расширения RemoveFor() для ModelState, смоделированный по HTML-помощникам MVC:

    public static void RemoveFor<TModel>(this ModelStateDictionary modelState, 
                                         Expression<Func<TModel, object>> expression)
    {
        string expressionText = ExpressionHelper.GetExpressionText(expression);

        foreach (var ms in modelState.ToArray())
        {
            if (ms.Key.StartsWith(expressionText + ".") || ms.Key == expressionText)
            {
                modelState.Remove(ms);
            }
        }
    }

Вот как это используется:

if (model.CheckoutModel.ShipToBillingAddress == true) 
{
    // REUSE BILLING ADDRESS FOR SHIPPING ADDRESS
    ShoppingCart.ShippingAddress = ShoppingCart.BillingAddress;

    // REMOVE MODELSTATE ERRORS FOR SHIPPING ADDRESS
    ModelState.RemoveFor<SinglePageStoreModel>(x => model.CheckoutModel.ShippingAddress);
}

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

15 голосов
/ 27 июля 2011

Если вы используете одну и ту же модель представления со свойством [Required] в двух разных контекстах: один, где свойство требуется, а другой - нет, вам придется вручную изменить ModelState какты делаешь.

Альтернативой является использование другой модели вида.Возможно, есть базовый класс со всеми свойствами, кроме требуемого рассматриваемого свойства.Затем извлеките из него две модели представлений: одну со свойством, где она требуется, а другую со свойством, где его нет (я знаю, это дублирование).Вы можете решить оставить их полностью отдельными и не использовать наследование.

11 голосов
/ 27 июля 2011

По сути, ваша проблема в том, что, хотя ваши классы украшены [Обязательно], это не всегда так.Если вы работаете в контексте, где это не так, вам действительно следует использовать класс, который не определяет свойство как [Обязательное].

Вы действительно должны использовать корректную ViewModelопределены для его конкретного использования, и это может означать дублирование некоторых классов.ViewModel связан с реализацией пользовательского интерфейса, и хотя он может использовать классы из вашей доменной модели, это не всегда является правильным решением.

В противном случае возможны варианты не использовать ModelState.IsValid,или продолжать использовать ModelState.Remove.

Но логично, что ваша ViewModel имеет смысл быть «проверяемой», чтобы не игнорировать определенные ошибки проверки.

1 голос
/ 21 ноября 2016

Если ваша собственность не всегда требуется, вы не должны украшать ее [Required].

Хорошей альтернативой для проверки является реализация интерфейса IValidatableObject .

Например, допустим, вы хотите, чтобы поле State было обязательным только в том случае, если для страны указано United States. Вы можете сделать это так:

public class AddressModel : IValidatableObject
{
    [Required]
    public string Country { get; set; }
    public string State { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if(Country == "United States" && String.IsNullOrEmpty(State))
        {
            yield return new ValidationResult("State is required for United States", new [] { nameof(State) });
        }
    }
}

Примечание. Этот вид проверки работает только на стороне сервера.

Другие альтернативы?

Как уже упоминалось в других ответах, иногда полезно создать 2 или более моделей, если представления и проверки очень разные.

1 голос
/ 27 июля 2011

Я полностью с Мистером Стивом Морганом

Так что если вашей ViewModel не всегда нужно какое-то свойство, чтобы быть Required, тогда вы не должны украшать его как Обязательное.

Я не знаю, почему вы хотите решить эту проблему, но я полагаю, что в некоторых случаях вам нужно PropertyOne, чтобы быть Required, если PropertyTwo имеет значение. В этом случае вам может понадобиться сделать CustomValidationAttribute чтобы проверить эти два свойства.

Я использую что-то вроде этого:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class PropertyNeededAttribute : ValidationAttribute
{
    private const string defaultErrorMessage = "'{0}' needs '{1}' to be valid.";

    public PropertyNeededAttribute(string originalProperty, string neededProperty)
        : base(defaultErrorMessage)
    {
        NeededProperty = neededProperty;
        OriginalProperty = originalProperty;
    }

    public string NeededProperty { get; private set; }
    public string OriginalProperty { get; private set; }

    public override object TypeId
    {
        get { return new object(); }
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                             OriginalProperty, NeededProperty);
    }

    public override bool IsValid(object value)
    {
        object neededValue = Statics.GetPropertyValue(value, NeededProperty);
        object originalValue = Statics.GetPropertyValue(value, OriginalProperty);
        if (originalValue != null && neededValue == null)
            return false;
        return true;
    }
}

примечание: Statics.GetPropertyValue(...) ничего не делаю, но получаю значение из свойства для сравнения.

Надеюсь, это помогло:)

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