14

I need to use a custom modelbinder of some kind to always treat incoming dates in UK format, i have setup a custom model binder and registered like so:

GlobalConfiguration.Configuration.BindParameter(typeof(DateTime), new DateTimeModelBinder());

This only seems to work for querystring parameters, and only works if i specify [ModelBinder] on my parameter like so, is there i way to make all actions use my model binder:

public IList<LeadsLeadRowViewModel> Get([ModelBinder]LeadsIndexViewModel inputModel)

Also, how can i get my posted form to my Api controller to use my model binder?

Dale K
  • 25,246
  • 15
  • 42
  • 71
Paul Hinett
  • 1,951
  • 2
  • 26
  • 40

4 Answers4

6

You can register a model binder globally by implementing a ModelBinderProvider and inserting it into the list of services.

Example use in Global.asax:

GlobalConfiguration.Configuration.Services.Insert(typeof(ModelBinderProvider), 0, new Core.Api.ModelBinders.DateTimeModelBinderProvider());

Below is example code demonstrating a ModelBinderProvider and a ModelProvider implemenation that converts DateTime arguments in a culture aware manner (using the current threads culture);

DateTimeModelBinderProvider implementation:

using System;
using System.Web.Http;
using System.Web.Http.ModelBinding;

...

public class DateTimeModelBinderProvider : ModelBinderProvider
{
    readonly DateTimeModelBinder binder = new DateTimeModelBinder();

    public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
    {
        if (DateTimeModelBinder.CanBindType(modelType))
        {
            return binder;
        }

        return null; 
    }
}

DateTimeModelBinder implementation:

public class DateTimeModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        ValidateBindingContext(bindingContext);

        if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName) ||
            !CanBindType(bindingContext.ModelType))
        {
            return false;
        }

        bindingContext.Model = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName)
            .ConvertTo(bindingContext.ModelType, Thread.CurrentThread.CurrentCulture);

        bindingContext.ValidationNode.ValidateAllProperties = true;

        return true;
    }

    private static void ValidateBindingContext(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException("bindingContext");
        }

        if (bindingContext.ModelMetadata == null)
        {
            throw new ArgumentException("ModelMetadata cannot be null", "bindingContext");
        }
    }

    public static bool CanBindType(Type modelType)
    {
        return modelType == typeof(DateTime) || modelType == typeof(DateTime?);
    }
}
jim.taylor.1974
  • 3,493
  • 1
  • 17
  • 11
5

I think you don't need a model binder. Your approach is incorrect. The right approach for dates is using a client side globalization library like the globalize library to parse dates formatted in any language and transform them into JavaScript date objects. Then you can serialize your datascript data structures in JSON with the browser JSON.stringify, and this should work. It is better to use always standard formats for dates and to use a formatter instead than a model binder. Available formatters handle also TimeZones correctly, if you use the kind parameter of your C# DateTime objects to specify if the date time is expressed in local time or in UTC time.

Dale K
  • 25,246
  • 15
  • 42
  • 71
Francesco Abbruzzese
  • 4,139
  • 1
  • 17
  • 18
  • how would i format all my dates to iso format on the client side before posting, do i need to do this manually before every post for every date field? seems a little tedious and repetitive? – Paul Hinett Oct 15 '12 at 14:55
  • Absolutely not! You can customize the JSON.stringify function of the browser by passing it a function that recognize dates and transform them in the adequate strings. Once you have done this you can just call JSON.stringify to converto all your js model in json with the right format for dates – Francesco Abbruzzese Oct 15 '12 at 15:23
  • Actually, the default behaviour of all main browser JSON.stringify function should be to transform dates into iso format...so you dont have to do anything:) – Francesco Abbruzzese Oct 15 '12 at 15:30
4

Attribute routing seems to conflict with model binding. If you use attribute routing, you can wrap the global.asax configuration into a single GlobalConfiguration.Config call to avoid the initialisation issues:

GlobalConfiguration.Configure(config =>
{
    config.BindParameter(typeof(DateTime), new DateTimeModelBinder());
    config.MapHttpAttributeRoutes();
}

(This might be fixed in the upcoming ASP.NET 5 if it's related to bug #1165.)

Pathoschild
  • 4,636
  • 2
  • 23
  • 25
0

You do not need a custom model binder for that. You should be good by changing the thread culture to UK or set your web.config settings for UK, if this is what your site is using all the time.

If not, you can still change the DateTimeFormatInfo for the CurrentCulture to UK one.

There is also a good post about model binders available here by Scott Hanselman.

You can put this in Global.asax:

ModelBinders.Binders[typeof(DateTime)] = new DateAndTimeModelBinder()  
Roland
  • 972
  • 7
  • 15
  • 4
    i have tried settings this in the web.config and it doesn't work for ApiControllers, if i post the same form to an mvc controller it works perfectly, but to an ApiController it doesn't seem to respect my culture settings. – Paul Hinett Sep 07 '12 at 22:14