ASP.NET Custom Model Binding: DateTime

Viewed 5149

The problem

At this point I have a problem where my Get action is trying to read a DateTime parameter in a diferent format that is sent.

While the DateTime sent has this format: 0:dd/MM/yyyy The Get Actions expects: 0:MM/dd/yyyy

The solution (maybe)

In order to change what the Get action is expecting I'm using a Custom Model Binding.

The GET Action

    public async Task<IActionResult> Details(int? id, [ModelBinder(typeof(PModelBinder))]DateTime date)

The ModelBinder class

Now here are a few things that are missing and I don't know how to complete it properly:

public class PModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        string theDate = bindingContext.HttpContext.Request.QueryString["date"]; 
        //What should I write inside the []? 
        //I've tried QueryString["date"] which is the name of the parameter but it says is wrong
        DateTime dt = new DateTime();
        bool success = DateTime.TryParse(date); //Should I apply ParseExact? How should I do it?
        if (success)
        {
            return new //what should I be returning here? dt?
        }

    }
}

I've several questions marked as comments in the code above since I'm just starting to understand Custom Model Binding. Hope anyone can give me some advice.

I'm following this article:

https://weblogs.asp.net/melvynharbour/mvc-modelbinder-and-localization

But it's from 2008!!!, Although it seems valid since it's exactly the problem I'm having with my GET Action (diferent date formats)

Update: aditional information

The parameter date is defined as:

    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
    public DateTime FechaLInicioLiq { get; set; }

and the URL build when calling that GET Action has this structure for the date parameter:

date=10%2F11%2F2017%200%3A00%3A00
1 Answers

You have several issues with your model binder implementation:

  1. Do not hardcode parameter name (date). Use bindingContext.ModelName instead.
  2. You should handle situation if value was not actually provided. You could check it by comparing result of IValueProvider.GetValue() with ValueProviderResult.None.

Here is sample DateTime model binder that accomplish what you need:

public class DateTimeModelBinder : IModelBinder
{
    private readonly IModelBinder baseBinder = new SimpleTypeModelBinder(typeof(DateTime));

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult != ValueProviderResult.None)
        {
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

            var valueAsString = valueProviderResult.FirstValue;

            //  valueAsString will have a string value of your date, e.g. '31/12/2017'
            var dateTime = DateTime.ParseExact(valueAsString, "dd/MM/yyyy", CultureInfo.InvariantCulture);
            bindingContext.Result = ModelBindingResult.Success(dateTime);

            return Task.CompletedTask;
        }

        return baseBinder.BindModelAsync(bindingContext);
    }
}
Related