asp.net-mvc Asp.net MVC 中的自定义日期时间模型绑定器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2356601/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 00:10:05  来源:igfitidea点击:

Custom DateTime model binder in Asp.net MVC

asp.net-mvcbindingmodelbinderscustom-model-binder

提问by Robert Koritnik

I would like to write my own model binder for DateTimetype. First of all I'd like to write a new attribute that I can attach to my model property like:

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

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

This is the easy part. But the binder part is a bit more difficult. I would like to add a new model binder for type DateTime. I can either

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

  • implement IModelBinderinterface and write my own BindModel()method
  • inherit from DefaultModelBinderand override BindModel()method
  • 实现IModelBinder接口并编写我自己的BindModel()方法
  • 继承DefaultModelBinder和覆盖BindModel()方法

My model has a property as seen above (Birth). So when the model tries to bind request data to this property, my model binder's BindModel(controllerContext, bindingContext)gets invoked. Everything ok, but. How do I get property attributes from controller/bindingContext, to parse my date correctly? How can I get to the PropertyDesciptorof property Birth?

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

Edit

编辑

Because of separation of concerns my model class is defined in an assembly that doesn't (and shouldn't) reference System.Web.MVC assembly. Setting custom binding (similar to Scott Hanselman's example) attributes is a no-go here.

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

采纳答案by Craig Stuntz

I don't think you should put locale-specific attributes on a model.

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

Two other possible solutions to this problem are:

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

  • Have your pages transliterate dates from the locale-specific format to a generic format such as yyyy-mm-dd in JavaScript. (Works, but requires JavaScript.)
  • Write a model binder which considers the current UI culture when parsing dates.
  • 让您的页面将日期从语言环境特定格式音译为通用格式,例如 JavaScript 中的 yyyy-mm-dd。(有效,但需要 JavaScript。)
  • 编写一个模型绑定器,在解析日期时考虑当前的 UI 文化。

To answer your actual question, the way to get custom attributes (for MVC 2) is to write an AssociatedMetadataProvider.

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

回答by gdoron is supporting Monica

you can change the default model binder to use the user culture using IModelBinder

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

And in the Global.Asax add the following to Application_Start():

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

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

Read more at this excellent blogthat describe why Mvc framework team implemented a default Culture to all users.

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

回答by Davide Vosti

I had this very big problem myself and after hours of try and fail I got a working solution like you asked.

我自己遇到了这个非常大的问题,经过数小时的尝试和失败,我得到了一个像你问的那样可行的解决方案。

First of all since having a binder on just a property is not possibile yuo have to implement a full ModelBinder. Since you don't want the bind all the single property but only the one you care you can inherit from DefaultModelBinder and then bind the single property:

首先,因为在一个属性上有一个活页夹是不可能的,所以你必须实现一个完整的 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);
        }
    }
}

In my example I'm parsing date with a fiexed culture, but what you want to do is possible. You should create a CustomAttribute (like DateTimeFormatAttribute) and put it over you property:

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

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

Now in the BindProperty method, instead of looking for a DateTime property you can look for a property with you DateTimeFormatAttribute, grab the format you specified in the constructor and then parse the date with DateTime.ParseExact

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

I hope this helps, it took me very long to come with this solution. It was actually easy to have this solution once I knew how to search it :(

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

回答by Phil

You could implement a custom DateTime Binder like so, but you have to take care about the assumed culture and value from the actual client request. May you get an Date like mm/dd/yyyy in en-US and want it to convert in the systems culture en-GB (which it would be like dd/mm/yyyy) or an invariant culture, like we do, then you have to parse it before and using the static facade Convert to change it in its behaviour.

您可以像这样实现自定义 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;
        }
    }

Anyway, culture dependend DateTime parsing in a stateless Application can by a cruelty...Especially when you work with JSON on javascript clientside and backwards.

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