7

I'm using Web API within ASP .NET MVC 4 RC, and I have a method that takes a complex object with nullable DateTime properties. I want the values of the input to be read from the query string, so I have something like this:

public class MyCriteria
{
    public int? ID { get; set; }
    public DateTime? Date { get; set; }
}

[HttpGet]
public IEnumerable<MyResult> Search([FromUri]MyCriteria criteria)
{
    // Do stuff here.
}

This works well if I pass a standard date format in the query string such as 01/15/2012:

http://mysite/Search?ID=1&Date=01/15/2012

However, I want to specify a custom format for the DateTime (maybe MMddyyyy)... for example:

http://mysite/Search?ID=1&Date=01152012

Edit:

I've tried to apply a custom model binder, but I haven't had any luck applying it to only DateTime objects. The ModelBinderProvider I've tried looks something like this:

public class DateTimeModelBinderProvider : ModelBinderProvider
{
    public override IModelBinder GetBinder(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(DateTime) || bindingContext.ModelType == typeof(DateTime?))
        {
            return new DateTimeModelBinder();
        }
        return null;
    }
}

// In the Global.asax
GlobalConfiguration.Configuration.Services.Add(typeof(ModelBinderProvider), new DateTimeModelBinderProvider());

The new model binder provider is created, but GetBinder is only called once (for the complex model parameter, but not for each property within the model). This makes sense, but I would like to find a way to make it to use my DateTimeModelBinder for DateTime properties, while using the default binding for non-DateTime properties. Is there a way to override the default ModelBinder and specify how each property is bound?

Thanks!!!

9
  • stackoverflow.com/questions/528545/… - Same problem, may help you. Commented Aug 9, 2012 at 4:07
  • This post applies to controller actions in MVC web pages, but I haven't been able to find the equivalent for Web API. I've tried registering a custom model binder for Web API, and I think it would work if my method took in separate parameters. But, I want it to take a complex object as input, and I haven't found a way to override the binding of DateTime properties while using the default binding for the others. Commented Aug 9, 2012 at 5:15
  • as you already correctly mention, you can solve it by custom binder? what exact problem are you having with it? Commented Aug 9, 2012 at 7:01
  • I would use a dedicated view-model instead of the domain-model to send info to views and model-bind back again. Then I'd set MyCriteriaViewModel.Date to type string, and handle the conversion when I map this to the domain-model (MyCriteria.Date) in the controller or a mapping layer. Commented Aug 9, 2012 at 7:31
  • @alexanderb I've updated my question to provide more details... I haven't figured out how to register the model binder for only certain Types of properties. Commented Aug 9, 2012 at 16:38

1 Answer 1

1

Consider setting your view-model's Date property to type string

Then either write a utility function to handle the mapping between the viewmodel type and the domain-model type:

public static MyCriteria MapMyCriteriaViewModelToDomain(MyCriteriaViewModel model){

    var date = Convert.ToDateTime(model.Date.Substring(0,2) + "/" model.Date.Substring(2,2) + "/" model.Date.Substring(4,2));

    return new MyCriteria
    {
        ID = model.ID,
        Date = date
    };

}

or use a tool like AutoMapper, like this:

in Global.asax

//if passed as MMDDYYYY:
Mapper.CreateMap<MyCriteriaViewModel, MyCriteria>().
    .ForMember(
          dest => dest.Date, 
          opt => opt.MapFrom(src => Convert.ToDateTime(src.Date.Substring(0,2) + "/" src.Date.Substring(2,2) + "/" src.Date.Substring(4,2)))
);

and in the controller:

public ActionResult MyAction(MyCriteriaViewModel model)
{
    var myCriteria = Mapper.Map<MyCriteriaViewModel, MyCriteria>(model);

    //  etc.
}

From this example it might not seem that AutoMapper is providing any added value. It's value comes when you are configuring several or many mappings with objects that generally have more properties than this example. CreateMap will automatically map properties with the same name and type, so it saves lots of typing and it's much DRYer.

Sign up to request clarification or add additional context in comments.

1 Comment

I've used AutoMapper and I like what it offers, but I would still prefer to apply this mapping before the action is executed. It seems cleaner that way, IMHO. I will also need to define an interface that the API controller implements. The interface will be used to dynamically create clients for the API. I would want the interface to be more explicit in defining the expectations, rather than accepting strings.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.