liant modèle personnalisé DateTime dans Asp.net MVC
-
23-09-2019 - |
Question
Je voudrais écrire mon propre modèle de liaison pour le type de DateTime
. Tout d'abord je voudrais écrire un nouvel attribut que je peux joindre à ma propriété de modèle comme:
[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}
Ceci est la partie facile. Mais la partie de liant est un peu plus difficile. Je voudrais ajouter un nouveau modèle de liaison pour le type DateTime
. Je peux soit
- mettre en œuvre l'interface
IModelBinder
et écrire ma propre méthodeBindModel()
- hériter de
DefaultModelBinder
et remplacer la méthode deBindModel()
Mon modèle a une propriété comme on le voit ci-dessus (Birth
). Ainsi, lorsque le modèle tente de lier les données de demande à cette propriété, s'invoqué le BindModel(controllerContext, bindingContext)
de mon classeur modèle. Tout va bien, mais. Comment puis-je obtenir les attributs propriété du contrôleur / BindingContext, pour analyser correctement ma date ? Comment puis-je accéder à la PropertyDesciptor
de Birth
de propriété?
Modifier
En raison de la séparation des préoccupations de ma classe de modèle est défini dans un ensemble qui n'a pas (et ne devrait pas) l'assemblage System.Web.Mvc de référence. Le réglage personnalisé de liaison (similaire à ) attributs est un non-droit ici.
La solution
Je ne pense pas que vous devriez mettre des attributs spécifiques aux paramètres régionaux sur un modèle.
Deux autres solutions possibles à ce problème sont:
- Demandez à vos pages translitération dates du format spécifique locale à un format générique tel que aaaa-mm-jj JavaScript. (Works, mais nécessite JavaScript.)
- Ecrire un liant modèle qui tient compte de la culture d'interface utilisateur actuelle lors de l'analyse des dates.
Pour répondre à votre question réelle, la façon d'obtenir des attributs personnalisés (pour MVC 2) est de écrire un AssociatedMetadataProvider.
Autres conseils
vous pouvez modifier le modèle de liaison par défaut à utiliser la culture de l'utilisateur à l'aide 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);
}
}
Et dans le Global.asax ajouter ce qui suit à Application_Start ():
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new NullableDateTimeBinder());
En savoir plus cet excellent blog que expliquer pourquoi l'équipe-cadre Mvc mis en œuvre une culture par défaut à tous les utilisateurs.
J'ai eu moi-même très gros problème et après les heures d'essai et ne parviennent que je suis une solution de travail comme vous avez demandé.
D'abord depuis avoir un liant sur juste une propriété n'est pas possibile yuo doivent mettre en œuvre une ModelBinder complète. Puisque vous ne voulez pas lier tous les biens unique, mais seulement celui que vous vous souciez pouvez hériter de DefaultModelBinder et se lient alors la seule propriété:
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);
}
}
}
Dans mon exemple, je suis analyse syntaxique date avec une culture fiexed, mais ce que vous voulez faire est possible. Vous devez créer un CustomAttribute (comme DateTimeFormatAttribute) et le mettre sur vous bien:
[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}
dans la méthode bindProperty, au lieu de chercher une propriété DateTime vous pouvez chercher une propriété avec vous DateTimeFormatAttribute, prenez le format que vous avez spécifié dans le constructeur, puis analyser la date avec DateTime.ParseExact
J'espère que cette aide, il m'a fallu beaucoup de temps pour venir avec cette solution. Il était en fait facile d'avoir cette solution une fois que je savais comment chercher le: (
Vous pouvez mettre en œuvre une coutume DateTime Binder comme si, mais vous devez prendre soin de la culture et de la valeur supposée de la demande réelle du client. Pouvez-vous obtenir une date comme mm / jj / aaaa en États-Unis et que vous voulez à convertir dans la culture des systèmes en-GB (ce qui serait comme jj / mm / aaaa) ou une culture invariante, comme nous le faisons, vous doivent analyser avant et utiliser Convert façade statique pour le changer dans son comportement.
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;
}
}
Quoi qu'il en soit, la culture dependend analyse syntaxique DateTime dans une application sans état peut par une cruauté ... Surtout quand vous travaillez avec JSON javascript clientside et en arrière.