Custom DateTime model binder in ASP.NET Core 1 (RTM)
Asked Answered
R

1

8

I'm writing and ASP.NET Core 1 application, which has a Web API Controller where supplier will post data.

This is a simplified version of the Model class to which I will bind the incoming data:

public class Lead
{
    public int SupplierId { get; set; }
    public DateTime Date { get; set; }
}

The problem is that the dates will be posted with German formatting, for instance, 30.09.2016. I do not want to set the global culture of the application to de-DE because a) I'm not German, and b) the rest of the application will work with ISO dates.

I've set to write a custom IModelBinder and, because it's apparently obligatory in ASP.NET Core MVC, an IModelBinderProvider.

Here's my implementation of the IModelBinderProvider:

public class GermanDateTimeModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
            throw new ArgumentNullException(nameof(context));

        if (!context.Metadata.IsComplexType && 
                context.Metadata.ModelType == typeof(DateTime))
            return new GermanDateTimeBinder();

        return null;
    }
}

The problem is that my IModelBinderProvider is only being hit for the Lead class, i.e., context.Metadata.ModelType == typeof(Lead).

I would expect the model binder to see that we're not dealing with a DateTime, move on, and then come back for every property of the Lead class.
This is apparently not the case, and my custom IModelBinder never get hit.

The reason I say that IModelBinderProvider appears to be obligatory no is that I've found no way of directly registering an IModelBinder into the MVC pipeline; I have to register an IModelBinderProvider instead:

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddMvc(options =>
    {
        options.ModelBinderProviders.Insert(0, new GermanDateTimeModelBinderProvider());
    });
}

How can I get ASP.NET Core MVC to apply my custom DateTime binder to the Date property of the Lead class? Is there maybe a way to skip the whole IModelBinderProvider business and use just an IModelBinder?

Relativize answered 30/9, 2016 at 14:50 Comment(0)
G
2

Do you post your values as JSON? If so, I'd suggest you register a custom JsonConverter with JSON.NET. In the converter you'll use a JsonSerializer with a different DateFormatString, in this case a German date format. All other JsonSerializer aren't affected by this.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services
        .AddMvc()
        .AddJsonOptions(option =>
        {
            option.SerializerSettings.Converters.Add(new LeadConverter());
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddDebug();

        app.UseMvcWithDefaultRoute();
    }
}

public class LeadConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new System.NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jsonSerializer = new JsonSerializer
        {
            DateFormatString = "dd.MM.yyyy"
        };

        return jsonSerializer.Deserialize<Lead>(reader);
    }

    public override bool CanConvert(Type objectType) => objectType == typeof(Lead);
}
Getz answered 3/10, 2016 at 12:26 Comment(1)
Thanks for your answer, it is indeed a very good approach. We do however have a workaround, with a DTO that has string insetad of DateTime, and then we use AutoMapper to parse the dates. So this question is not so much about how can I work around the problem, but how to actually implement a custom ModelBinder in ASp.NET Core 1 :)Relativize

© 2022 - 2024 — McMap. All rights reserved.