我一直遇到将短日期格式绑定到DateTime模型属性的同样问题。在查看了许多不仅仅与DateTime有关的示例之后,我总结出了以下内容:
using System;
using System.Globalization;
using System.Web.Mvc;
namespace YourNamespaceHere
{
public class CustomDateBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext", "controllerContext is null.");
if (bindingContext == null)
throw new ArgumentNullException("bindingContext", "bindingContext is null.");
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (value == null)
throw new ArgumentNullException(bindingContext.ModelName);
CultureInfo cultureInf = (CultureInfo)CultureInfo.CurrentCulture.Clone();
cultureInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
try
{
var date = value.ConvertTo(typeof(DateTime), cultureInf);
return date;
}
catch (Exception ex)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
return null;
}
}
}
public class NullableCustomDateBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext", "controllerContext is null.");
if (bindingContext == null)
throw new ArgumentNullException("bindingContext", "bindingContext is null.");
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (value == null) return null;
CultureInfo cultureInf = (CultureInfo)CultureInfo.CurrentCulture.Clone();
cultureInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
try
{
var date = value.ConvertTo(typeof(DateTime), cultureInf);
return date;
}
catch (Exception ex)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
return null;
}
}
}
}
为了保持路由等在 Global ASAX 文件中注册的方式,我还在我的 MVC4 项目的 App_Start 文件夹中添加了一个名为 CustomModelBinderConfig 的新的静态类:
using System;
using System.Web.Mvc;
namespace YourNamespaceHere
{
public static class CustomModelBindersConfig
{
public static void RegisterCustomModelBinders()
{
ModelBinders.Binders.Add(typeof(DateTime), new CustomModelBinders.CustomDateBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new CustomModelBinders.NullableCustomDateBinder());
}
}
}
我只需要在Global ASASX的Application_Start中调用静态的RegisterCustomModelBinders方法,方法如下:
我然后只需像这样调用静态的RegisterCustomModelBinders:
protected void Application_Start()
{
CustomModelBindersConfig.RegisterCustomModelBinders();
}
这里需要注意的是,如果您将一个DateTime值写入隐藏域,就像这样:
@Html.HiddenFor(model => model.SomeDate) // a DateTime property
@Html.Hiddenfor(model => model) // a model that is of type DateTime
我这样做后,页面上实际值的格式为"MM/dd/yyyy hh:mm:ss tt",而不是我想要的"dd/MM/yyyy hh:mm:ss tt"。这导致我的模型验证失败或返回错误的日期(显然交换了日和月的值)。
经过大量的思考和尝试失败后,解决方案是在 Global.ASAX 中通过设置每个请求的区域性信息来实现:
protected void Application_BeginRequest()
{
CultureInfo cInf = new CultureInfo("en-ZA", false);
// NOTE: change the culture name en-ZA to whatever culture suits your needs
cInf.DateTimeFormat.DateSeparator = "/";
cInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
cInf.DateTimeFormat.LongDatePattern = "dd/MM/yyyy hh:mm:ss tt";
System.Threading.Thread.CurrentThread.CurrentCulture = cInf;
System.Threading.Thread.CurrentThread.CurrentUICulture = cInf;
}
如果将它放在Application_Start甚至Session_Start中,它都不会起作用,因为那会将它分配给会话的当前线程。正如你所知,Web应用程序是无状态的,因此服务于先前请求的线程与服务于当前请求的线程不同,因此你的区域性信息已经消失在数字天空的大GC中。
感谢:
Ivan Zlatev - http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/
garik - https://dev59.com/RUzSa4cB1Zd3GeqPoaQz#2468447
Dmitry - https://dev59.com/uXRB5IYBdhLWcg3wvpmc#11903896