【发布时间】:2015-09-16 16:18:52
【问题描述】:
我有一个带有自定义 RequiredIf DataAnnotation 实现的 C# 应用程序,如下所示:
public class RequiredIf : RequiredAttribute {
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
public RequiredIf (String property_name, Object desired_value) {
PropertyName = property_name;
DesiredValue = desired_value;
}
protected override ValidationResult IsValid (object value, ValidationContext context) {
Object instance = context.ObjectInstance;
Type type = instance.GetType();
Object prop = type.GetProperty(PropertyName);
Object property_value = type.GetProperty(PropertyName).GetValue(instance, null);
if ( property_value.ToString() == DesiredValue.ToString() ) {
ValidationResult result = base.IsValid(value, context);
return result;
}
return ValidationResult.Success;
}
}
这似乎在几乎所有情况下都有效。
我有一个要验证的 ViewModel。 ViewModel 有一个子类。基本实现是:
public class ViewModel {
.........
public class ChildObject {
[RequiredIf("FieldToCheck", false)] // note that it's only required if the value is FALSE!
DateTime? ConditionalField { get; set; } // could be NULL
Boolean FieldToCheck { get; set; }
}
..........
}
当我提交表单并将 FieldToCheck 设置为 true 时,我的 ViewModel 验证在此 RequiredIf 上仍然失败。我可以在控制器中查看 ViewModel 变量中的值并验证它是true。但是,我可以在 DataAnnotation 的 IsValid 方法中设置断点,然后在调试器中看到 FieldToCheck 是 false - 不是我提交的!
Model Binder 绑定错了吗?如果是这样,为什么?还有为什么validator中的绑定值不正确,而controller中正确?
这导致我的 ViewModel 验证失败每当 DateTime 字段留空(提交 NULL)。但是,如果该布尔值是错误的,我什至不会在我的表单中显示 DateTime 字段——我不希望用户在该字段中提交任何内容。
编辑:这似乎只发生在布尔值上,但无论在哪里创建布尔值/条件字段,它都会始终如一地发生。
模型绑定器是否在验证之前不绑定布尔成员?
EDIT2:
在视图中,我有一个用于发布数据的标准表单:
@Model ViewModel
@using ( Html.BeginForm("Create", "Controller", FormMethod.Post ) {
.........
@Html.DropDownListFor(m => m.FieldToCheck, Model.FieldToCheckList)
.........
<button type="submit">Submit</button>
}
控制器:
public class Controller : Controller {
[HttpPost]
public ActionResult Create (ViewModel view_model) {
// I set a breakpoint here, and if I submit FieldToCheck = true, the debugger shows that FieldToCheck is, indeed, true.
if ( ModelState.IsValid ) {
// save to db
return View();
}
return View();
}
}
我有一个方法可以在渲染表单之前初始化 ViewModel 中的字段,并在其中设置FieldToCheckList 如下:
view_model.FieldToCheckList = new List<SelectListItem> {
new SelectListItem { Text = "Yes", Value = Boolean.TrueString },
new SelectListItem { Text = "No", Value = Boolean.FalseString }
}
所以我使用select 来填充值。但是,我尝试了多个其他表单元素(包括将 HiddenFor 静态设置为 true),但仍然会导致相同的问题。如果我在RequiredIf 验证器的IsValid 方法中设置断点,那么无论提交的数据如何,我的所有布尔值都是假的。如果我在Controller 的Create 方法中设置断点,我的布尔值设置正确。
我还测试了Boolean 和bool 数据类型。好像没什么影响。
EDIT4:
我实际上并没有弄清楚问题的原因或真正的解决方案,但我确实找到了解决方法。
所以,显然问题发生在我执行RequiredIf(AnyBooleanValue, AnythingButTrue) 时。如果 DesiredValue 设置为 anything 除了true,则模型绑定器会将 ViewModel 中的 all 布尔值设置为默认值 - false - 不管是什么已提交。我尝试了多种方法-RequiredIf(FieldToCheck, "False")、RequiredIf(FieldToCheck, !true) 等。没有任何效果。但是如果我这样做RequiredIf(FieldToCheck, true),值就会正确绑定!
所以解决方法是添加一个新字段:
public class ViewModel {
public Boolean FieldToCheck { get; set; }
public Boolean IsConditionalFieldRequired { get; set; }
[RequiredIf("IsConditionalFieldRequired",true)]
public string ConditionalField { get; set; }
}
在视图中,我将IsConditionalFieldRequired 作为隐藏字段,并且每当更改FieldToCheck 时,我使用jQuery 将IsConditionalFieldRequired 设置为FieldToCheck 的倒数。
(我将其添加为编辑而不是答案,因为我认为这不是一个真正的解决方案,只是一个可行的解决方法。显然这不是完成工作的最雄辩的方式。)
【问题讨论】:
-
您是使用 mvc 发布表单还是通过 javascript 发布表单?
-
我正在使用标准 HTML 表单发布数据。所以是的,我正在使用 MVC。似乎模型绑定器没有正确绑定布尔值,因为调试器(在 IsValid 方法中)显示 all 布尔值设置为 false;但是,这些值在控制器方法中被正确绑定。
-
可以添加相关的html吗?
-
检查
if ( property_value.ToString() == DesiredValue.ToString() ) {中的值 - 可能是一个案例问题 -"False"与"false"- 但实际使用布尔值而不是字符串值会好得多 -
请注意,一旦您使用 C# 6,请切换到
nameof(FieldToCheck)而不是"FieldToCheck",以便您获得类型感知和重命名安全性!
标签: c# asp.net-mvc entity-framework validation