Etc.<\/li>\n<\/ul>\nThese approaches feels a bit hacky and cumbersome to me, whereas I\u2019ve created an alternative approach.<\/p>\n
An alternative approach<\/h2>\n I often tend to include an enum type to my abstract classes that defines the underlying concrete type. Something similar to:<\/p>\n
<\/p>\n
Since the model\u2019s type is present as a value, why not opt-in and guide the DefaultModelBinder what the concrete type of the model is (based on the enum type value provided in the request) and then let the binder continue do its magic.<\/p>\n
[codesyntax lang=”asp”]<\/p>\n
public class CustomModelBinder<TEnum> : DefaultModelBinder\n where TEnum : struct\n{\n private readonly string _key;\n private readonly Dictionary<TEnum, Type> _typeMapper;\n\n public CustomModelBinder(string key, Dictionary<TEnum, Type> typeMapper)\n {\n _key = key;\n _typeMapper = typeMapper;\n }\n\n public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)\n {\n ValueProviderResult value = bindingContext.ValueProvider.GetValue(_key);\n\n TEnum @enum;\n if (value == null || !Enum.TryParse(value.AttemptedValue, true, out @enum)\n || !_typeMapper.ContainsKey(@enum))\n {\n return base.BindModel(controllerContext, bindingContext);\n }\n\n bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, _typeMapper[@enum]);\n return base.BindModel(controllerContext, bindingContext);\n }\n}\n<\/pre>\n[\/codesyntax]<\/p>\n
The CustomModelBinder hooks into the binding process by overrid\u00edng the BindModel method of the DefaultModelBinder. The custom binder updates the binding context\u2019s metadata with the concrete type, based on the type value in the request, and then the DefaultModelBinder continue its work.<\/p>\n
The ConcreteModelBinderAttribute is created to decorate in-parameters and serves the purpose to instantiate the correct model binder. One attribute should be created per abstract model.<\/p>\n
[codesyntax lang=”asp”]<\/p>\n
public class ConcreteModelBinderAttribute : CustomModelBinderAttribute\n{\n private readonly string _key;\n\n public ConcreteModelBinderAttribute(string key = \"type\")\n {\n _key = key;\n }\n\n public override IModelBinder GetBinder()\n {\n return new CustomModelBinder(_key, new Dictionary<ConcreteType, Type>\n {\n {ConcreteType.A, typeof (ConcreteA)},\n {ConcreteType.B, typeof (ConcreteB)}\n });\n }\n}\n<\/pre>\n[\/codesyntax]<\/p>\n
For the sake of this example, I just added a dictionary that serves as a type map but one could change this to use an IoC-container, ServiceLocator, or similar instead.<\/p>\n
Example<\/h2>\n So if we create an MVC controller as follows (the in-parameter is decorated with the ConcreteModelBinder described previously): \n[codesyntax lang=”c#”]<\/p>\n
public class ExampleController : Controller\n{\n [HttpPost]\n public ActionResult Index([ConcreteModelBinder]Base model)\n {\n return Json(model);\n }\n}\n<\/pre>\n[\/codesyntax]<\/p>\n
And use the marvellous chrome extension Postman to create a POST request to the server. We get the following result:<\/p>\n
<\/p>\n
The model binder now instantiate the correct concrete class, the only thing we have to remember is to provide the type in the request to the server.<\/p>\n
Great success.<\/p>\n","protected":false},"excerpt":{"rendered":"
It is common to have abstract models but it can be a bit of struggle to make it work together with MVC\u2019s built in model binder. I guess a lot of people have faced the following yellow screen of death (\u201cCannot create an abstract class\u201d)<\/p>\n","protected":false},"author":2,"featured_media":14827,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[118,119],"tags":[],"class_list":["post-16134","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","category-news"],"yoast_head":"\n
Binding abstract models in ASP.NET MVC - Infozone - English<\/title>\n \n \n \n \n \n \n \n \n \n \n \n \n \n\t \n\t \n\t \n