Подтвердить что ты не робот

Это правильный способ использования ModelState.Remove для работы с ModelState?

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Здесь мое решение - метод расширения 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);
}

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

Ответ 3

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

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

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

Но логически, ваш ViewModel имеет смысл быть "validatable", а не игнорировать некоторые ошибки проверки.

Ответ 4

Я полностью согласен с Mr.Steve Morgan

Итак, если вашему 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;
    }
}

note: Statics.GetPropertyValue(...) ничего не делать, кроме как получить значение свойства, чтобы сравнить его.

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

Ответ 5

Если ваше свойство не всегда требуется, вы не должны его украшать [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 или более модели, если взгляды и проверки сильно отличаются.