使用ModelState.Remove处理ModelState是否正确?

时间:2011-07-27 10:55:30

标签: asp.net-mvc asp.net-mvc-3 modelstate

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

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

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

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

5 个答案:

答案 0 :(得分: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);
}

因此,在回答你的问题时,我相信肯定有一些用例,这是正确的方法,这样的强类型助手让它看起来更好 - 如果你是更容易证明关注很多魔法弦。

答案 1 :(得分:15)

如果您在两个不同的上下文中使用具有[Required]属性的相同视图模型,一个需要属性,另一个不需要属性,那么您需要手动更改{{ 1}}正如你所做的那样。

另一种方法是使用不同的视图模型。也许有一个基类,除了所讨论的必需属性外,还有所有属性。然后从中导出两个视图模型,一个具有所需的属性,另一个具有不属性的属性(我知道它是重复的)。您可以决定将它们完全分开,而不是使用继承。

答案 2 :(得分:11)

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

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

如果不这样做,选项要么不使用ModelState.IsValid,要么继续使用ModelState.Remove。

但从逻辑上讲,将ViewModel设置为“可验证”是合理的,而不必忽略某些验证错误。

答案 3 :(得分:1)

我完全和Mr.Steve Morgan一起

因此,如果您的ViewModel并不总是需要某个属性为Required,那么您不应该将其装饰为必需。

我不知道你为什么要这个问题,但我想在某些情况下,如果PropertyOne有价值,你需要RequiredPropertyTwo 在这种情况下,您可能需要让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(...)什么都不做,只是从属性中获取值来进行比较。

希望这有助于:)

答案 4 :(得分:1)

如果您的财产并非总是需要,则不应使用# -*- coding: utf-8 -*- import smtplib from email.mime.text import MIMEText class SMTPClient(object): def __init__(self, recepient, subject, body): self.recepient = recepient self.subject = subject self.body = body self.mail_host = conf.get('smtp_server.host') self.mail_port = conf.get('smtp_server.port') self.username = conf.get('account.username') self.password = conf.get('account.password') self.mail_sender = conf.get('account.from') self._setup() def _setup(self): self.smtp_client = smtplib.SMTP(self.mail_host, self.mail_port) self.smtp_client.ehlo_or_helo_if_needed() self.smtp_client.starttls() self.smtp_client.login(self.username, self.password) self._send_mail() def _make_mail(self): message = MIMEText(self.body, _charset='utf-8') message['From'] = self.mail_sender message['To'] = self.recepient message['Subject'] = self.subject return message.as_string() def send_mail(self): self.smtp_client.sendmail(self.mail_sender, self.recepient, self._make_mail()) self.smtp_client.quit() 进行装饰。

进行验证的另一个好方法是实现接口IValidatableObject

例如,假设您希望仅在国家/地区[Required]时才需要字段State。你可以这样做:

United States

注意:此类验证仅适用于服务器端。

其他替代方案?

正如其他答案中所提到的,如果视图和验证非常不同,创建2个或更多模型有时是个好主意。