23

我正在开发一个大型 MVC3 Web 应用程序,并且对该ModelState.IsValid方法感到烦恼。

我几乎所有的控制器都使用了 ModelState 来验证发布的数据。这些视图都基于包含不同类的 ViewModel,这些类显然包含可以标记为的属性[Required]

我遇到的问题是有时不需要所需的属性,我必须使用该ModelState.Remove方法才能ModelState.IsValid实现。

我的问题是通过使用ModelState.Remove,这是正确的做事方式还是有更有效的方法。

4

5 回答 5

20

这是我的解决方案 -RemoveFor()上的扩展方法ModelState,仿照 MVC HTML 助手:

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

因此,在回答您的问题时,我相信肯定有这样的用例是正确的方法,并且像这样的强类型助手使它看起来更好看 - 如果您担心很多,也更容易证明的魔法弦。

于 2012-12-23T03:54:56.037 回答
18

如果您[Required]在两种不同的上下文中使用具有属性的相同视图模型,一种是需要该属性,另一种是不需要该属性,那么您需要ModelState在执行过程中手动更改。

另一种方法是使用不同的视图模型。可能有一个基类,其中包含所有属性,但有问题的必需属性除外。然后从中派生两个视图模型,一个具有需要的属性,一个具有不需要的属性(我知道这是重复的)。您可能决定将它们完全分开而不使用继承。

于 2011-07-27T11:00:11.120 回答
11

从根本上说,您的问题是,虽然您的课程用 [Required] 装饰,但并非总是如此。如果您在不正确的上下文中操作,那么您应该真正使用一个没有将属性定义为 [必需] 的类。

您应该真正使用为其特定用途正确定义的 ViewModel,这可能意味着复制一些类。ViewModel 与 UI 的实现相关联,虽然它可能使用您的域模型中的类,但这并不总是正确的做法。

否则,选项是不使用 ModelState.IsValid,或者继续使用 ModelState.Remove。

但从逻辑上讲,您的 ViewModel 是“可验证的”是有意义的,而不必忽略某些验证错误。

于 2011-07-27T11:00:59.080 回答
1

我完全支持史蒂夫·摩根先生

因此,如果您的 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(...)除了从属性中获取值来比较它,什么都不做。

希望这有帮助:)

于 2011-07-27T12:59:36.670 回答
1

如果您的财产并不总是需要,则不应使用[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 个或更多模型有时是个好主意。

于 2016-11-21T18:59:20.077 回答