25

我想为DateTime类型编写自己的模型活页夹。首先,我想编写一个可以附加到模型属性的新属性,例如:

[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}

这是简单的部分。但是活页夹部分要困难一些。我想为 type 添加一个新的模型绑定器DateTime。我可以

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

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

编辑

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

4

4 回答 4

87

您可以使用 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);
    }
}

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

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new NullableDateTimeBinder());

在这个优秀的博客上阅读更多内容,该博客描述了为什么 Mvc 框架团队为所有用户实现了默认文化。

于 2011-11-07T11:11:24.383 回答
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);
        }
    }
}

在我的示例中,我正在使用固定文化解析日期,但您想要做的事情是可能的。您应该创建一个 CustomAttribute(如 DateTimeFormatAttribute)并将其放在您的属性上:

[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}

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

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

于 2010-06-11T06:33:36.240 回答
3

我认为您不应该将特定于语言环境的属性放在模型上。

这个问题的另外两个可能的解决方案是:

  • 让您的页面将日期从特定于语言环境的格式音译为通用格式,例如 JavaScript 中的 yyyy-mm-dd。(有效,但需要 JavaScript。)
  • 编写一个模型绑定器,在解析日期时考虑当前的 UI 文化。

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

于 2010-03-01T15:22:12.897 回答
0

您可以像这样实现自定义 DateTime Binder,但您必须注意实际客户请求中假定的文化和价值。愿您在 en-US 中获得类似 mm/dd/yyyy 的日期,并希望它转换为系统文化 en-GB(类似于 dd/mm/yyyy)或不变的文化,就像我们一样,那么您必须先解析它并使用静态外观 Convert 来改变它的行为。

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

            var resDateTime = new DateTime();

            if (valueResult == null) return null;

            if ((bindingContext.ModelType == typeof(DateTime)|| 
                bindingContext.ModelType == typeof(DateTime?)))
            {
                if (bindingContext.ModelName != "Version")
                {
                    try
                    {
                        resDateTime =
                            Convert.ToDateTime(
                                DateTime.Parse(valueResult.AttemptedValue, valueResult.Culture,
                                    DateTimeStyles.AdjustToUniversal).ToUniversalTime(), CultureInfo.InvariantCulture);
                    }
                    catch (Exception e)
                    {
                        modelState.Errors.Add(EnterpriseLibraryHelper.HandleDataLayerException(e));
                    }
                }
                else
                {
                    resDateTime =
                        Convert.ToDateTime(
                            DateTime.Parse(valueResult.AttemptedValue, valueResult.Culture), CultureInfo.InvariantCulture);
                }
            }
            bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
            return resDateTime;
        }
    }

无论如何,无状态应用程序中的文化依赖 DateTime 解析可能会很残酷......尤其是当您在 javascript 客户端和向后使用 JSON 时。

于 2013-09-05T07:54:16.003 回答