我正在开发一个大型的MVC3 Web应用程序,对ModelState.IsValid
方法感到烦恼。
我的几乎所有控制器都使用ModelState,以验证发布的数据。
视图都基于包含不同类的ViewModel,这些类显然包含可以标记为[Required]
的属性。
我遇到的问题是有时不需要所需的属性,我必须使用ModelState.Remove
方法,以便ModelState.IsValid
成为现实。
我的问题是使用ModelState.Remove
,这是正确的做事方式还是更有效的方法。
答案 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
有价值,你需要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(...)
什么都不做,只是从属性中获取值来进行比较。
希望这有助于:)
答案 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个或更多模型有时是个好主意。