Mam ten sam problem z powiązaniem formatu krótkiej daty z właściwościami modelu DateTime. Po zapoznaniu się z wieloma różnymi przykładami (nie tylko dotyczącymi DateTime) zestawiłem następujące:
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;
}
}
}
}
Aby zachować sposób, w jaki trasy itp są rejestrowane w globalnym pliku ASAX, dodałem również nową klasę sytatic do folderu App_Start mojego projektu MVC4 o nazwie 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());
}
}
}
Następnie po prostu wywołuję statyczne RegisterCustomModelBinders z mojej Global ASASX Application_Start w następujący sposób:
protected void Application_Start()
{
CustomModelBindersConfig.RegisterCustomModelBinders();
}
Ważną informacją jest to, że jeśli napiszesz wartość DateTime w ukrytym polu, jak to:
@Html.HiddenFor(model => model.SomeDate)
@Html.Hiddenfor(model => model)
Zrobiłem to, a rzeczywista wartość na stronie była w formacie „MM / dd / rrrr gg: mm: ss tt” zamiast „dd / MM / rrrr gg: mm: ss tt”, tak jak chciałem. To spowodowało, że walidacja mojego modelu zakończyła się niepowodzeniem lub zwróceniem niewłaściwej daty (oczywiście zamieniając wartości dnia i miesiąca).
Po wielu drapaniu w głowę i nieudanych próbach rozwiązaniem było ustawienie informacji o kulturze dla każdego żądania, robiąc to w Global.ASAX:
protected void Application_BeginRequest()
{
CultureInfo cInf = new CultureInfo("en-ZA", false);
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;
}
Nie zadziała, jeśli umieścisz go w Application_Start lub nawet Session_Start, ponieważ przypisuje go to do bieżącego wątku sesji. Jak dobrze wiesz, aplikacje internetowe są bezstanowe, więc wątek, który wcześniej obsługiwał twoje żądanie, nie jest tym samym wątkiem obsługującym twoje bieżące żądanie, dlatego informacje o kulturze trafiły do wielkiego GC w cyfrowym niebie.
Podziękowania dla: Ivan Zlatev - http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/
garik - https://stackoverflow.com/a/2468447/578208
Dmitry - https://stackoverflow.com/a/11903896/578208