【问题标题】:MVC Complex Model BindingMVC 复杂模型绑定
【发布时间】:2023-03-18 06:17:01
【问题描述】:

我想对包含对象列表的表单进行复杂的验证。

我的表单包含一个列表,比如说,MyObjects。 MyObject 由一个 double 数量和一个 MyDate 组成,它只是 DateTime 的一个包装器。

public class MyObject
{
    public MyDate Date { get; set; } //MyDate is wrapper around DateTime
    public double Price { get; set; }
}

表格...

<input type="text" name="myList[0].Date" value="05/11/2009" />
<input type="text" name="myList[0].Price" value="100,000,000" />

<input type="text" name="myList[1].Date" value="05/11/2009" />
<input type="text" name="myList[1].Price" value="2.23" />

这是我的行动

public ActionResult Index(IList<MyObject> myList)
{
   //stuff
}

我想允许用户输入 100,000,000 作为价格,并允许自定义模型绑定器去除“,”,以便它可以转换为双精度。同样,我需要将 05/11/2009 转换为 MyDate 对象。我想过创建一个 MyObjectModelBinder,但不知道从那里做什么。

ModelBinders.Binders[typeof(MyObject)] = new MyObjectModelBinder();

任何帮助表示赞赏。

【问题讨论】:

    标签: asp.net-mvc binding validation


    【解决方案1】:

    这是自定义模型绑定器的示例实现:

    public class MyObjectModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            // call the base method and let it bind whatever properties it can
            var myObject = (MyObject)base.BindModel(controllerContext, bindingContext);
            var prefix = bindingContext.ModelName;
            if (bindingContext.ValueProvider.ContainsKey(prefix + ".Price"))
            {
                string priceStr = bindingContext.ValueProvider[prefix + ".Price"].AttemptedValue;
                // priceStr = 100,000,000 or whatever the user entered
                // TODO: Perform transformations on priceStr so that parsing works
                // Note: Be carefull with cultures
                double price;
                if (double.TryParse(priceStr, out price))
                {
                    myObject.Price = price;
                }
            }
    
            if (bindingContext.ValueProvider.ContainsKey(prefix + ".Date"))
            {
                string dateStr = bindingContext.ValueProvider[prefix + ".Date"].AttemptedValue;
                myObject.Date = new MyDate();
                // TODO: Perform transformations on dateStr and set the values 
                // of myObject.Date properties
            }
    
            return myObject;
        }
    }
    

    【讨论】:

    • 谢谢,我自己也在做类似的事情。如果验证失败,我是否只需在 bindingContext 上使用 AddModelError? MyObject 上还有 5 个其他字符串属性。有没有更好的方法来遍历属性而不是 if contains key prefix + etc...?
    • 是的,如果验证失败,您可以在 bindingContext 上使用 AddModelErrorSetModelValue。就其他属性而言,您可以执行以下操作:var myObject = (MyObject)base.BindModel(controllerContext, bindingContext); 并让默认模型绑定器进行实例化并绑定它可以做的任何事情,而不是手动新建 MyObject
    • 创建 MyDateModelBinder 比在一个大的活页夹中单独绑定每个属性要好得多。 DefaultModelBinder 将自动将 MyDateModelBinder 用于 MyDate 类型的属性。
    • @queen3,这取决于,MyDate 是自定义类型,假设它没有公共设置器和/或默认公共构造函数。默认模型绑定器不知道如何实例化此类。
    • 当我添加 base.BindModel 时,它会添加“需要一个值”,因为它试图错误地绑定到双精度值。另外,我注意到如果发生错误,则不会保留 Price 或 MyDate 的值。
    【解决方案2】:

    你肯定走在正确的道路上。当我这样做时,我制作了一个中间视图模型,将 Price 作为字符串,因为逗号。然后我从视图模型(或表示模型)转换为控制器模型。控制器模型有一个非常简单的构造函数,它接受视图模型并且可以Convert.ToDecimal("12,345,678.90") 价格值。

    【讨论】:

      猜你喜欢
      • 2016-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多