asp.net mvc Liaison MVC DateTime avec un format de date incorrect




asp.net-mvc localization (8)

Asp.net-MVC permet maintenant la liaison implicite des objets DateTime. J'ai une action dans le sens de

public ActionResult DoSomething(DateTime startDate) 
{ 
... 
}

Cela convertit avec succès une chaîne d'un appel ajax dans un DateTime. Cependant, nous utilisons le format de date jj / MM / aaaa; MVC convertit en MM / jj / aaaa. Par exemple, envoyer un appel à l'action avec une chaîne '09 / 02/2009 'donne un DateTime de '02 / 09/2009 00:00:00', ou le 2 septembre dans nos paramètres locaux.

Je ne veux pas rouler mon propre classeur modèle pour un format de date. Mais il semble inutile de devoir changer l'action pour accepter une chaîne, puis utiliser DateTime.Parse si MVC est capable de le faire pour moi.

Est-il possible de modifier le format de date utilisé dans le classeur de modèle par défaut pour DateTime? Le classeur par défaut ne devrait-il pas utiliser vos paramètres de localisation?


  public class DateTimeFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.RequestType == "GET")
        {

            foreach (var parameter in filterContext.ActionParameters)
            {
                var properties = parameter.Value.GetType().GetProperties();

                foreach (var property in properties)
                {
                    Type type = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

                    if (property.PropertyType == typeof(System.DateTime) || property.PropertyType == typeof(DateTime?))
                    {
                        DateTime dateTime;

                        if (DateTime.TryParse(filterContext.HttpContext.Request.QueryString[property.Name], CultureInfo.CurrentUICulture, DateTimeStyles.None, out dateTime))
                            property.SetValue(parameter.Value, dateTime,null);
                    }
                }

            }
        }
    }
}

Il convient également de noter que même sans créer votre propre classeur modèle, plusieurs formats différents peuvent être analysables.

Par exemple, aux États-Unis, toutes les chaînes suivantes sont équivalentes et automatiquement liées à la même valeur DateTime:

/ entreprise / presse / mai% 2001 202008

/ entreprise / presse / 2008-05-01

/ entreprise / presse / 05-01-2008

Je suggère fortement d'utiliser yyyy-mm-dd parce que c'est beaucoup plus portable. Vous ne voulez vraiment pas gérer plusieurs formats localisés. Si quelqu'un réserve un vol le 1er mai au lieu du 5 janvier, vous aurez de gros problèmes!

NB: Je ne sais pas exactement si aaaa-mm-jj est universellement analysé dans toutes les cultures alors peut-être quelqu'un qui sait peut ajouter un commentaire.


Je viens de trouver la réponse à cette question avec une recherche plus exhaustive:

Melvyn Harbour a une explication complète de la raison pour laquelle MVC travaille avec les dates comme il le fait, et comment vous pouvez le remplacer si nécessaire:

http://weblogs.asp.net/melvynharbour/archive/2008/11/21/mvc-modelbinder-and-localization.aspx

Lors de la recherche de la valeur à analyser, le cadre recherche dans un ordre spécifique à savoir:

  1. RouteData (pas indiqué ci-dessus)
  2. Chaîne de requête URI
  3. Formulaire de demande

Seulement le dernier d'entre eux sera conscient de la culture cependant. Il y a une très bonne raison à cela, du point de vue de la localisation. Imaginez que j'ai écrit une application web montrant les informations de vol que je publie en ligne. Je recherche des vols à une certaine date en cliquant sur un lien pour ce jour (peut-être quelque chose comme http://www.melsflighttimes.com/Flights/2008-11-21 ), puis je veux envoyer ce lien à mon collègue dans les Etats Unis. La seule façon de garantir que nous regarderons la même page de données est de savoir si InvariantCulture est utilisé. En revanche, si j'utilise un formulaire pour réserver mon vol, tout se passe dans un cycle serré. Les données peuvent respecter le CurrentCulture lorsqu'il est écrit dans le formulaire, et doivent donc le respecter lors du retour du formulaire.


J'ai défini CurrentCulture et CurrentUICulture mon contrôleur de base personnalisé

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
    }

Je définirais globalement vos cultures. ModelBinder ramasse ça!

  <system.web>
    <globalization uiCulture="en-AU" culture="en-AU" />

Ou vous changez simplement ceci pour cette page.
Mais globalement dans web.config je pense que c'est mieux


J'ai mis la config ci-dessous sur mon MVC4 et cela fonctionne comme un charme

<globalization uiCulture="auto" culture="auto" />

Ça va être légèrement différent dans MVC 3.

Supposons que nous ayons un contrôleur et une vue avec la méthode Get

public ActionResult DoSomething(DateTime dateTime)
{
    return View();
}

Nous devrions ajouter ModelBinder

public class DateTimeBinder : IModelBinder
{
    #region IModelBinder Members
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        DateTime dateTime;
        if (DateTime.TryParse(controllerContext.HttpContext.Request.QueryString["dateTime"], CultureInfo.GetCultureInfo("en-GB"), DateTimeStyles.None, out dateTime))
            return dateTime;
        //else
        return new DateTime();//or another appropriate default ;
    }
    #endregion
}

et la commande dans Application_Start () de Global.asax

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

J'ai eu le même problème avec la liaison de format de date courte aux propriétés du modèle DateTime. Après avoir regardé de nombreux exemples différents (pas seulement concernant DateTime), j'ai rassemblé les suivants:

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;
            }
        }
    }
}

Pour conserver la façon dont les routes etc sont enregistrées dans le fichier Global ASAX, j'ai également ajouté une nouvelle classe sytatic dans le dossier App_Start de mon projet MVC4 nommé 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());
        }
    }
}

J'appelle juste alors le RegisterCustomModelBinders statique de mon ASASX Application_Start global comme ceci:

protected void Application_Start()
{
    /* bla blah bla the usual stuff and then */

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

Une note importante ici est que si vous écrivez une valeur DateTime à un champ caché comme ceci:

@Html.HiddenFor(model => model.SomeDate) // a DateTime property
@Html.Hiddenfor(model => model) // a model that is of type DateTime

Je l'ai fait et la valeur réelle sur la page était au format "MM / jj / aaaa hh: mm: ss tt" au lieu de "jj / mm / aaaa hh: mm: ss tt" comme je le voulais. Cela a provoqué l'échec de la validation de mon modèle ou le retour de la mauvaise date (en inversant évidemment les valeurs du jour et du mois).

Après beaucoup de grattage de la tête et de tentatives infructueuses, la solution consistait à définir les informations de culture pour chaque requête en faisant cela dans le fichier 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;
}

Cela ne fonctionnera pas si vous le collez dans Application_Start ou même Session_Start car cela l'affecte au thread en cours pour la session. Comme vous le savez bien, les applications web sont sans état, donc le sujet qui a répondu à votre demande est le même que celui de votre demande actuelle, d'où votre information culturelle est allée au grand GC dans le ciel numérique.

Merci à: Ivan Zlatev - http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/

garik - https://.com/a/2468447/578208

Dmitry - https://.com/a/11903896/578208





model-binders