1

在我的项目中,我有一个使用另一个类的模型类,如下面的示例所示。模型中的属性之一取决于对子对象的一个​​属性的验证——在此示例中,LastName 属性取决于对 Address.PostalCode 属性值的验证。我实现了一个自定义验证属性来验证我的 LastName 属性,它工作得很好。

public class User
{
    public static ValidationResult ValidateLastName(string lastName, ValidationContext context)
    {
        // Grab the model instance
        var user = context.ObjectInstance as User;
        if (user == null)
            throw new NullReferenceException();

        // Cross-property validation
        if (user.Address.postalCode.Length < 10000)
            return new ValidationResult("my LastName custom validation message.");

        return ValidationResult.Success;
    }

    [Display(Name = "Last name")]
    [CustomValidationAttribute(typeof(User), "ValidateLastName")]
    public string LastName { get; set; }

    [Display(Name = "First name")]
    public string FirstName { get; set; }

    [Display(Name = "Address:")]
    [CustomValidationAttribute(typeof(User), "ValidateAddress")]
    public AddressType Address { get; set; }
}

public class AddressType
{
    public string streetName = "";

    public string streetNumber = "";
    public string postalCode = "";
}

问题在于控制器中的 Address 属性不是从视图中构造的,并且它始终为 null。在此示例中,无论我在视图中发送什么,user.Address 始终为空。这是控制器代码。

    [HttpPost]
    public ActionResult Create(User user)
    {
        if (ModelState.IsValid)
        {
            // creation code here
            return RedirectToAction("Index");
        }
        else
        {
            return View(user);
        }
    }

这是视图:

        <div class="editor-label">
            @Html.LabelFor(model => model.Address.postalCode)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Address.postalCode)
            @Html.ValidationMessageFor(model => model.Address.postalCode)
        </div>

为了解决这个问题,我创建了一个自定义的虚拟绑定器,将视图中的字段映射到模型中的属性,如下所示:

public class UserBinder : IModelBinder
{
    private string GetValue(ModelBindingContext bindingContext, string key)
    {
        var result = bindingContext.ValueProvider.GetValue(key);
        return (result == null) ? null : result.AttemptedValue;
    }

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        User instance = new User();
        instance.FirstName = GetValue(bindingContext, "FirstName"); //controllerContext.HttpContext.Request["FirstName"];
        instance.LastName = GetValue(bindingContext, "LastName"); //controllerContext.HttpContext.Request["LastName"];

        instance.Address = new AddressType();
        string streetName = controllerContext.HttpContext.Request["Address.streetName"];

        //ModelStateDictionary mState = bindingContext.ModelState;
        //mState.Add("LastName", new ModelState { });
        //mState.AddModelError("LastName", "There's an error.");

        instance.Address.streetName = streetName;
                    ...
        return instance;
    }

活页夹工作正常,但验证属性不再工作。我认为必须有比这更好的绑定方法,是吗?

这个活页夹只是将 LastName 映射到 LastName 并将 Address.streetName 映射到 Address.streetName,我想应该有一种方法可以实现这一点,而不必编写所有这些繁琐的代码,也不会破坏自定义验证机制。

4

2 回答 2

2

您需要使用属性而不是公共字段才能使默认模型绑定正常工作。

AddressType将您的班级更改为:

public class AddressType
{
    public string streetName { get; set; }
    public string streetNumber { get; set; }
    public string postalCode { get; set; }
}
于 2012-09-07T14:58:49.023 回答
1

一种解决方案是在我的子类中使用属​​性而不是公共字段——感谢 Oded 的回答!

另一种解决方案是在控制器中调用 TryValidateModel,即使存在活页夹,这也可以启用我的验证代码。

    [HttpPost]
    public ActionResult Create(User user)
    {
        if (TryValidateModel(user))
        {
            // creation code here
            return RedirectToAction("Index");
        }
        else
        {
            return View(user);
        }
    }
于 2012-09-07T15:21:20.720 回答