自定义DataAnnotation绑定错误的值

时间:2015-09-16 16:18:53

标签: c# asp.net-mvc entity-framework validation

我有一个带有自定义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绑定错了吗?如果是这样,为什么?为什么验证器中的绑定值不正确,但控制器中的正确

这导致我的ViewModel验证失败每当 DateTime字段留空(提交NULL)。但是如果那个布尔值为false,我甚至不会在我的表单中显示 DateTime字段 - 我不希望用户在该字段中提交任何内容。< / p>

编辑:这似乎只发生了与布尔,但无论布尔/条件字段的创建位置如何,它都会一直发生。

Model Binder在验证之前是否未绑定布尔成员?

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),但仍然会导致同样的问题。如果我在IsValid验证器中的RequiredIf方法中设置断点,则无论提交的数据如何,我的所有布尔值都为false。如果我在Create中的Controller方法中设置断点,则我的布尔值设置正确。

我还测试了Booleanbool数据类型。它似乎没有任何影响。

EDIT4:

我实际上并未找出问题的原因或真正的解决方案,但我确实找到了解决方法。

所以,显然当我RequiredIf(AnyBooleanValue, AnythingButTrue)时会出现问题。如果将DesiredValue设置为除<{1}}之外的任何,则Model Binder会将ViewModel中的所有布尔值设置为默认值 - {{1 - 无论提交什么。我尝试了多种方法 - truefalse等。无效。但是,如果我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; } } 时,我都会使用jQuery将IsConditionalFieldRequired设置为FieldToCheck的倒数。< / p>

(我将其添加为编辑而不是答案,因为我认为这不是一个真正的解决方案,只是一个可行的解决方法。显然,这不是最有说服力的完成任务的方法。)

1 个答案:

答案 0 :(得分:1)

这是一个古老的问题,但是我今天遇到了一个同样的问题,当时我使用的是RequiredIf属性,而ValidationContext.ObjectInstance属性没有正确的绑定值。

在我看来,这是由于我的视图模型类具有可设置一些默认属性值的构造函数。在自定义属性代码中,属性值是在构造函数中设置的,而在控制器中,属性值来自发布的表单。

我通过删除构造函数并在视图中设置默认值来解决此问题。