Asp.net MVC中的自定义DateTime模型绑定器

Rob*_*nik 23 asp.net-mvc binding modelbinders custom-model-binder

我想为DateTime类型编写自己的模型绑定器.首先,我想写一个我可以附加到我的模型属性的新属性,如:

[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}
Run Code Online (Sandbox Code Playgroud)

这是简单的部分.但是粘合剂部分有点困难.我想为类型添加一个新的模型绑定器DateTime.我也可以

  • 实现IModelBinder接口并编写我自己的BindModel()方法
  • 继承DefaultModelBinder和覆盖BindModel()方法

我的模型有如上所示的属性(Birth).因此,当模型尝试将请求数据绑定到此属性时,BindModel(controllerContext, bindingContext)将调用我的模型绑定器.一切都好,但是.如何从controller/bindingContext获取属性属性,以正确解析我的日期?我怎样才能到达PropertyDesciptor房产Birth

编辑

由于关注点的分离,我的模型类是在一个没有(也不应该)引用System.Web.MVC程序集的程序集中定义的.设置自定义绑定(类似于Scott Hanselman的示例)属性是禁止的.

gdo*_*ica 83

您可以使用IModelBinder更改默认模型绑定器以使用用户区域性

public class DateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

        return value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
    }
}

public class NullableDateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

        return value == null
            ? null 
            : value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
    }
}
Run Code Online (Sandbox Code Playgroud)

在Global.Asax中将以下内容添加到Application_Start():

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new NullableDateTimeBinder());
Run Code Online (Sandbox Code Playgroud)

这个出色的博客上阅读更多信息,该博客描述了Mvc框架团队为所有用户实施默认文化的原因.

  • 先生给你的帽子小姐. (2认同)

Dav*_*sti 14

我自己遇到了这个非常大的问题,经过几个小时的尝试和失败,我得到了一个像你问的工作解决方案.

首先,因为在一个属性上只有一个绑定器是不可能的,所以必须实现一个完整的ModelBinder.由于您不希望绑定所有单个属性,只需要您关心的属性,您可以从DefaultModelBinder继承,然后绑定单个属性:

public class DateFiexedCultureModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
    {
        if (propertyDescriptor.PropertyType == typeof(DateTime?))
        {
            try
            {
                var model = bindingContext.Model;
                PropertyInfo property = model.GetType().GetProperty(propertyDescriptor.Name);

                var value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);

                if (value != null)
                {
                    System.Globalization.CultureInfo cultureinfo = new System.Globalization.CultureInfo("it-CH");
                    var date = DateTime.Parse(value.AttemptedValue, cultureinfo);
                    property.SetValue(model, date, null);
                }
            }
            catch
            {
                //If something wrong, validation should take care
            }
        }
        else
        {
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的例子中,我正在用一种文化来解析日期,但你想做的事情是可能的.您应该创建一个CustomAttribute(如DateTimeFormatAttribute)并将其放在您的属性上:

[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}
Run Code Online (Sandbox Code Playgroud)

现在在BindProperty方法中,您可以使用DateTimeFormatAttribute查找属性,获取在构造函数中指定的格式,然后使用DateTime.ParseExact解析日期,而不是查找DateTime属性.

我希望这有所帮助,我花了很长时间才得到这个解决方案.一旦我知道如何搜索它,实际上很容易有这个解决方案:(


Cra*_*ntz 3

我认为您不应该将特定于区域设置的属性放在模型上。

此问题的另外两种可能的解决方案是:

  • 让您的页面将日期从特定于区域设置的格式音译为通用格式,例如 JavaScript 中的 yyyy-mm-dd。(可以工作,但需要 JavaScript。)
  • 编写一个模型绑定器,在解析日期时考虑当前的 UI 区域性。

要回答您的实际问题,获取自定义属性(对于 MVC 2)的方法是编写 AssociatedMetadataProvider