Je passe un objet du client au serveur. Les propriétés de l'objet qui sont représentées par string.empty sont converties en null au cours de ce processus. Je me demandais comment empêcher cela lorsque le type d'objets prend en charge string.empty.
console.log("DataToPost:", dataToPost);
$.ajax({
type: "POST",
contentType: 'application/json'
url: "../../csweb/Orders/SaveOrderDetails/",
data: dataToPost,
success: function (result) {
console.log(result);
},
error: function (e) {
console.error(e);
}
});
Mon modèle comprend des objets DateTime nullables. Je ne peux pas forcer toutes les valeurs nulles à string.empty sur le serveur.
J'utilise AutoMapper, donc je préférerais ne pas avoir à inspecter les propriétés individuellement sur le serveur.
Il s'agit d'une fonctionnalité MVC qui lie des chaînes vides à null
s.
Cette logique est contrôlée avec la propriété ModelMetadata.ConvertEmptyStringToNull qui est utilisée par la DefaultModelBinder
.
Vous pouvez définir ConvertEmptyStringToNull
avec l'attribut DisplayFormat
public class OrderDetailsModel
{
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Comment { get; set; }
//...
}
Cependant, si vous ne souhaitez pas annoter toutes les propriétés, vous pouvez créer un classeur de modèle personnalisé où vous le définissez sur false:
public class EmptyStringModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
Binders = new ModelBinderDictionary() { DefaultBinder = this };
return base.BindModel(controllerContext, bindingContext);
}
}
Et vous pouvez utiliser le ModelBinderAttribute dans votre action:
public ActionResult SaveOrderDetails([ModelBinder(typeof(EmptyStringModelBinder))]
OrderDetailsModel orderDetailsModel)
{
}
Ou vous pouvez le définir globalement comme le ModelBinder par défaut dans votre Global.asax:
ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();
Vous pouvez en savoir plus sur cette fonctionnalité ici .
Au lieu de créer un ModelBinder qui modifie le ModelMetadata comme certaines réponses le suggèrent, une alternative plus propre consiste à fournir un ModelMetadataProvider personnalisé.
public class EmptyStringDataAnnotationsModelMetadataProvider : System.Web.Mvc.DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
modelMetadata.ConvertEmptyStringToNull = false;
return modelMetadata;
}
}
Puis dans Application_Start ()
ModelMetadataProviders.Current = new EmptyStringDataAnnotationsModelMetadataProvider();
La réponse acceptée n'a pas fonctionné pour moi en utilisant MVC4. Cependant, la solution de contournement suivante le fait et j'ai pensé que cela aiderait les autres.
public class CustomModelBinder : DefaultModelBinder
{
public bool ConvertEmptyStringToNull { get; set; }
public CustomModelBinder ()
{
}
public CustomModelBinder (bool convertEmptyStringToNull)
{
this.ConvertEmptyStringToNull = convertEmptyStringToNull;
}
protected override bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// this little bit is required to override the ConvertEmptyStringToNull functionality that we do not want!
foreach (string propertyKey in bindingContext.PropertyMetadata.Keys)
{
if(bindingContext.PropertyMetadata[propertyKey] != null)
bindingContext.PropertyMetadata[propertyKey].ConvertEmptyStringToNull = this.ConvertEmptyStringToNull;
}
return base.OnModelUpdating(controllerContext, bindingContext);
}
}
Cela résoudra le problème sous MVC4 +. Il semblerait que bindingContext.ModelMetadata.ConvertEmptyStringToNull est complètement ignoré, et cela parce que le paramètre existe dans l'objet PropertyMetadata pour chaque propriété liée. PropertyMetadata est recréé dans BindProperty () donc si vous le définissez avant cet appel de méthode, il sera écrasé sauf s'il existe en tant qu'attribut sur la propriété de votre objet lié (tel que [DisplayFormat (ConvertEmptyStringToNull = false)])). Personne ne veut le faire sur chaque propriété car c'est idiot.
Le problème est que AutoMapper transforme nullables en null lorsque la chaîne est vide. Cette autre question a été répondue par quelque chose qui, je crois, suffit à vos besoins: Chaîne null Automapper à vider
Lors de la publication de données avec $.ajax
, null
n'est pas une valeur possible pour une propriété de l'option data
. Si vous regardez la demande avec un débogueur http, vous verrez qu'elle est convertie en une chaîne vide.
Je suppose donc que votre contrôleur MVC applique la conversion opposée.
Ce que je fais dans une application ajax pour résoudre ce problème, c'est que je n'utilise pas l'option data
de $.ajax()
, mais je sérialise tout dans JSON et le place dans un seul champ "data "de l'option de données. Comme ça, vous n'avez pas de problèmes avec des valeurs nulles. Bien sûr, vous devez désérialiser côté serveur.