views:

35

answers:

2

Hi Guys,

If i have the following strongly-typed view:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<XXX.DomainModel.Core.Locations.Location>" %>

Where Location is an abstract class.

And i have the following Controller, which accepts a strongly-typed Model via a POST:

[HttpPost]
public ActionResult Index(Location model)

I get a runtime error stating "Cannot Create Abstract Class

Which of course makes sense. However - i'm not sure what the best solution is here.

I have many concrete types (around 8), and this is a view where you can only edit properties of the abstract class.

What i've tried to do is create overloads for all the different concrete types, and perform my logic in a common method.

[HttpPost]
public ActionResult Index(City model)
{
   UpdateLocationModel(model);
   return View(model);
}

[HttpPost]
public ActionResult Index(State model)
{
   UpdateLocationModel(model);
   return View(model);
}

etc etc

And then:

[NonAction]
private void UpdateLocationModel (Location model)
{
   // ..snip - update model
}

But this doesn't work either, MVC complains the action methods are ambiguous (also makes sense).

What do we do? Can we simply not bind to an abstract model?

A: 

Just to throw it out there - I'm very much interested in what others might answer, but this is what I ended up doing in the case where I had a similar situation;

Basically, I did not use the model class as a parameter in the Action method, instead passing in FormCollection and testing a couple known discriminators to figure out which type to create/edit, then used TryUpdateModel from there.

It seemed there might be a better way, but I'd never gotten around to thinking about it more.

Andrew Barber
Yeah, that was my next step! :) But awww, i wan't the strongly typed goodness. :)
RPM1984
It's delicious, and nutritious; Part of this complete breakfast!
Andrew Barber
+1  A: 

How about writing a custom model binder for this abstract class:

public class CustomBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        // TODO: based on some request parameter choose the proper child type
        // to instantiate here
        return new Child();
    }
}

This make sense only if you have a form where input elements are inserted dynamically based on some user action. In this case you need to pass some additional parameter to indicate which concrete class you need. Otherwise I would stick to concrete view models as action parameters.

Darin Dimitrov
Well this is a very simple view, edit fields for properties on the abstract model. So i didn't want to have to "duplicate" this HTML across multiple strongly-typed views. I'll have a look into custom model binders - as i do know the child type when i render the view. I'll try it out the office tomorrow - cheers.
RPM1984
Okay i've had a read of model binders, and i agree - it doesn't make sense in my scenario. I'm going to stick with the concrete view models. This would work though, so i'll accept your answer. Thanks.
RPM1984