0

我将 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 字段并自己转换它们,但如果有的话,我真的很想避免这种情况模型绑定器已经具备为我执行此操作的能力。

4

2 回答 2

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());
于 2013-07-22T19:58:34.373 回答
1

这个问题没有得到一口咬,所以我查看了默认的模型活页夹,看看幕后发生了什么。它经历了许多阶段来查看一个值是否可以转换为 ViewModel 类型,但其中大多数对我来说是无法访问的。我确实找到了一段代码,它回退到使用我以前从未使用过的类型转换器。

使用这个MSDN Type Converter how-to,我做了一个简单的转换器,并用适当的属性装饰了我的类,它就可以工作了。我不确定性能影响是什么,但它确实简化了我的 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);
   }

   ...
}

就是这样。到目前为止,它按预期工作。

于 2013-07-22T19:26:13.633 回答