将输入字段绑定到自定义对象而不是字符串

时间:2013-07-22 17:35:47

标签: asp.net-mvc asp.net-mvc-4 controller

我正在使用ASP.NET MVC 4作为内部Web应用程序,我希望将HTML输入字段绑定到自定义对象而不是字符串。

在HTML中我有输入字段,如下所示:

<input type="hidden" name="First" value="1;Simple" />
<input type="hidden" name="First" value="2;Sample" />
<input type="hidden" name="Second" value="1;Over" />
<input type="hidden" name="Third" value="22;Complex" />
<input type="hidden" name="Third" value="17;Whosit" />

这将很高兴地绑定到ViewModel属性,如:

public string[] First { get; set; }
public string[] Second { get; set; }
public string[] Third { get; set; }

每个字符串都是键+值的分隔字符串,我很乐意将其自动解析为具体对象(我已经定义了一个。)理想情况下,我希望它完全按照上面的方式绑定但是使用我的对象会知道如何将分隔的字符串拆分为适当的属性。

我无法弄清楚如何让MVC绑定到自定义对象。我已经使用了构造函数和隐式运算符定义,但除了字符串数据类型之外,我无法使用它。

我知道如果我在HTML中预先将值拆分成对,但是我使用的JavaScript库不能提供此功能,那么我可以使用它。例如,我知道重复{name} .Label和{name} .Value将工作绑定到我的复杂对象上的字符串属性,但这是禁止的,并且是非启动的。

我已经使用自定义对象来处理文件上传,但我怀疑这只是因为它继承自同一个基础对象。我不能这样做,因为string是密封类型,不能扩展。

我最后的办法是找到默认的模型绑定器代码并反映出来,以确定它是如何分配值以查看它是否教会了我可以覆盖的任何内容。我宁愿不去自定义绑定器的路线,我必须自己编写,如果归结为它,我将只有重复的ViewModel字段并自己转换它们但我真的很想避免这种情况,如果有的话已经有一个模型绑定器能够为我做这个。

2 个答案:

答案 0 :(得分:3)

这是你能做的。假设您的MyThing类是这样的:

public class MyThing
{
    public int Id { get; set; }
    public string Name { get; set; }

    public override string ToString()
    {
        return string.Format("{0};{1}", this.Id, this.Name);
    }
}

然后,您可以为它创建自定义模型绑定器,如下所示:

public class MyModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        ValueProviderResult valueResult = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName);
        ModelState modelState = new ModelState { Value = valueResult };
        object actualValue = null;

        if (valueResult != null && !string.IsNullOrEmpty(valueResult.AttemptedValue))
        {
            if(valueResult.AttemptedValue.Contains(';'))
            {

                try
                {
                    var attemptedValue = valueResult.AttemptedValue.Split(';');
                    int id = int.Parse(attemptedValue.First());
                    string name = attemptedValue.Last();
                    actualValue = new MyThing { Id = id, Name = name };
                }
                catch(Exception e)
                {
                    modelState.Errors.Add(e);
                }
            }
            else
            {
                modelState.Errors.Add("Invalid value.");
            }

            bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
        }

        return actualValue;
    }
}

您需要在Global.asax的Application_Start事件中注册您的ModelBinder,如下所示:

ModelBinders.Binders.Add(typeof(MyThing), new MyModelBinder());

答案 1 :(得分:1)

这个问题没有得到任何影响,所以我查看了默认的模型活页夹,看看幕后发生了什么。它可以通过多个阶段来查看是否可以将值转换为ViewModel类型,但是大多数都无法访问。我确实找到了一段代码,这些代码回归到使用我以前从未使用过的类型转换器。

使用这个MSDN Type Converter操作方法,我创建了一个简单的转换器,并用适当的属性装饰我的类,它只是起作用。我不确定性能影响是什么,但它确实简化了我的ViewModel代码。

以下示例对我有用。请记住,我只是从DefaultModelBinder使用的简单字符串类型进行转换,所以看起来它看起来并不多,但它解决了我的需要,并教会了我框架的一个新功能。

public class MyThingConverter : TypeConverter
{
   public override bool CanConvertFrom(ITypeDescriptorContext context,
      Type sourceType)
   {
      if (sourceType == typeof(string))
         return true;

      return base.CanConvertFrom(context, sourceType);
   }

   public override object ConvertFrom(ITypeDescriptorContext context,
      CultureInfo culture, object value)
   {
      if (value is string)
         return new MyThing((string)value);

      return base.ConvertFrom(context, culture, value);
   }
}

[TypeConverter(typeof(MyThingConverter))]
public class MyThing
{
   public MyThing(string combinedValue)
   {
      //Split combinedValue into whatever properties I need
      ...
   }

   public override string ToString()
   {
      return string.Format("{0};{1}", prop1, prop2);
   }

   ...
}

就是这样。到目前为止,它的工作正如预期的那样。