Model binding to an interface using default behavior
asp.net-mvc, asp.net-web-api, c#, model-binding, parameters
Solution
As I mentioned above, the interface parameters of the action method can be resolved using DI. But the interface members of that object need to be handled differently.
I created 2 types of JSON converters, a single entity type and a collection type, to decorate the interface properties.
Here's a class that needs to be resolved as an action interface parameter.
public class CreateEnvelopeModel : ICreateEnvelopeCommand
{
[JsonConverter(typeof(EntityModelConverter<CreateEmailModel, ICreateEmailCommand>))]
public ICreateEmailCommand Email { get; set; }
[JsonConverter(typeof(CollectionEntityConverter<CreateFormModel, ICreateFormCommand>))]
public IList<ICreateFormCommand> Forms { get; set; }
}
Here's the controller action method
public HttpResponseMessage PostEnvelope(ICreateEnvelopeCommand model)
{
// do stuff
}
Here are the 2 JSON converters
public class EntityModelConverter<T, Tt> : JsonConverter where T : Tt
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Tt));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<T>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value, typeof(T));
}
}
public class CollectionEntityConverter<T, Tt> : JsonConverter where T : Tt
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IList<Tt>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
IList<Tt> items = serializer.Deserialize<List<T>>(reader).Cast<Tt>().ToList();
return items;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value, typeof(IList<T>));
}
}
Problem
I'm trying to bind a controller action to an interface but still maintain the default binding behavior. ``` public class CoolClass : ISomeInterface { public DoSomething {get;set;} // ISomeInterface } public class DosomethingController : ApiController { public HttpResponseMessage Post(ISomeInterface model) { // do something with model which should be an instance of CoolClass } } ``` The consumer of my service knows nothing of CoolClass so having them add "$type" to the JSON they are passing would be a hack in my opinion. I'd like to be able to handle it in the service. If I specify CoolClass as the action parameter it works fine. I found a partial solution to my question here Dependency injection for ASP.NET Web API action method parameters but there is a follow up issue. That solution does not resolve interface properties. See my example below: IConcreteClass will be resolved, but ISubtype will not. ``` public class SubConcreteClass : ISubtype { // properties } public class ConcreteClass : IConcreteClass { public ISubtype Subtype {get;set;} } ``` Once the media formatter sees that is can resolve the type in IConcreteClass, it then reads the entire stream. So I'm guessing there is no chance to resolve interface members.