Default Model Binder does not bind for Nullable types in IEnumerable
Asked Answered
H

3

15

I have a controller action whose definition looks like-

public ActionResult ChangeModel( IEnumerable<MyModel> info, long? destinationId)

And the model:

public class MyModel
{
    public string Name; //Gets populated by default binder
    public long? SourceId; //remains null though the value is set when invoked
}

The 'Name' property gets populated in the controller action however the SourceId property remains null. The destinationId which is a long? parameter gets populated as well.

While stepping through the MVC (version 2) source code this is the exception thrown by DefaultModelBinder.

The parameter conversion from type 'System.Int32' to type 'System.Nullable`1[[System.Int64, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' failed because no type converter can convert between these types.

If the model is changed to long instead of long?, the default model binder sets the value.

public class MyModel
{
    public string Name {get;set;}; //Gets populated by default binder
    public long SourceId {get;set;}; //No longer long?, so value gets set
}

Is this a known issue? Since the MVC source code is optimized, I am not able to step through most of the code.

Update: The request being sent is a Http POST using Json with the source JSon resembling -

{"info":[{"Name":"CL1","SourceId":2}], "destinationId":"1"}
Haley answered 2/5, 2011 at 13:21 Comment(0)
W
4

Maybe it's too late, but I have found a workaround. You can convert the SourceId field to string before sending data. So your JSON data will look like

{"info":[{"Name":"CL1","SourceId":"2"}], "destinationId":"1"}

This worked in my situation (Int32 -> decimal?, ASP NET MVC 3)

Wizardry answered 18/10, 2011 at 5:22 Comment(0)
I
2

I would recommend you to use properties instead of fields on your view model:

public class MyModel
{
    public string Name { get; set; }
    public long? SourceId { get; set; }
}

Now the following request:

/somecontroller/changemodel?destinationId=123&info[0].Name=name1&info[0].SourceId=1&info[1].Name=name2&info[1].SourceId=2

Populates the model fine.

Incommunicative answered 2/5, 2011 at 19:4 Comment(3)
They were originally properties in the view model. I have updated the code and please note that I am doing a post using JSon.Haley
@QED, by default there is no Json provider for requests in ASP.NET MVC 2. It is built-in ASP.NET MVC 3. So what you are using to parse the JSON request into your model?Incommunicative
I am making use of the JsonValueProviderFactory available in the MVCFutures. The values seem to get set de-serialized okay and is also set in the backing store.Haley
M
1

The Default Model Binder is parsing all SourceId values as ints. But it seems .NET is missing a default type converter from int to long?.

What I would do is implementing a type converter for that case.

Morass answered 2/5, 2011 at 13:35 Comment(2)
I am not sure if the type converter needs to be put in, as the other input parameter in the action is a long? (destinationId) and that seems to be populating fine.Haley
@QED: Yes, I'm aware of that - the problem seems to be specific to the IEnumerable<> binding. Still, that's what I would try; I'm supposing that the default model binder explicitly does all conversion between simple integral types and their nullable counterparts, but does not do that correctly for collections. And yes, that means that another possible solution would be override the default model binder...Morass

© 2022 - 2024 — McMap. All rights reserved.