繁体   English   中英

如何将复杂类绑定到视图,同时保留我的自定义验证属性?

[英]how can I bind a complex class to a view, while preserving my custom validation attributes?

在我的项目中,我有一个使用另一个类的模型类,如下面的示例所示。 模型中的一个属性取决于对子对象属性的验证 - 在此示例中,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始终为null。 这是控制器代码。

    [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,我想应该有一种方法可以实现这一点,而不必编写所有这些繁琐的代码而不破坏自定义验证机制。

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

将您的AddressType类更改为:

public class AddressType
{
    public string streetName { get; set; }
    public string streetNumber { get; set; }
    public string postalCode { get; set; }
}

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

另一个解决方案是在控制器中调用TryValidateModel,即使存在绑定器,也可以启用我的验证代码。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM