3

My controller action should be usable for a set of models that inherit the abstract class Polis:

public abstract class Polis
{
    /// <summary>
    /// Fields
    /// </summary>

    protected Polis()
    {
    }

    public Polis(Object input)
    {
        // Use input
    }
}

My controler action specifies the generic type should inherit this abstract class. But it does not see the constructor of the abstract class that has an argument. So I have to specify that it implements 'new()', instead I would like to use the constructor with an argument.

    public virtual ActionResult SavePolis<TModel>(PolisPostModel polisPM) where TModel : Polis, new()
    {
        if (ModelState.IsValid)
        {
            // Get the object or save a new object in the database
        }

        return Json(new
        {
            success = ModelState.IsValid,
            status = this.GetStatus(polisPM),
        });
    }

All data handling is done inside the inhereting classes, so I need the methods of the inheriting classes to be executed. But when I try to call the controller action giving my specific type as argument it has the error "No overload for method 'SavePolis' takes 0 arguments":

@Html.Hidden("SaveMyPolis", Url.Action(MVC.Controller.SavePolis<MyPolis>())

So what is the correct way to call this? And is it possible that the inherited class is made fully available, so it's methods are called instead of those from the abstract class.

MrFox
  • 4,852
  • 7
  • 45
  • 81

2 Answers2

1

I would use a custom modelbinder for this and multiple action methods accepting the subclasses. Assuming two subclasses of Polis: SubPolisA and SubPolisB, I would have 2 action methods:

  1. public ActionResult SavePolis(SubPolisA model)
  2. public ActionResult SavePolis(SubPolisA model)

then I would define a custom modelbinder for polis:

public class PolisModelBinder : System.Web.Mvc.IModelBinder
{
   public object BindModel(ControllerContext controllerContext, 
                            ModelBindingContext bindingContext)
    {
         var form = controllerContext.HttpContext.Request.Form;
         //use hidden value to determine the model
         if(form.Get("PolisType") == "SubClassA") 
         {
            //bind SubPolisA
         }
         else 
         {
            //bind SubPolisB
         }
    }
}

then in Application_Start() I would register the model binder using

ModelBinders.Binders.Add(typeof(SubPolisA), new PolisModelBinder());
ModelBinders.Binders.Add(typeof(SubPolisB), new PolisModelBinder());

or use the ModelBinderAttribute. If you want to you could also use multiple model binders for each subclass.

*sorry for code formatting

Devolus
  • 21,661
  • 13
  • 66
  • 113
korir
  • 51
  • 4
0

I believe all you need to do is this:

@Html.Hidden("SaveMyPolis", Url.Action(MVC.Controller.SavePolis<MyPolis>(Model))

assuming the model type in your View is PolisPostModel.

dav_i
  • 27,509
  • 17
  • 104
  • 136
  • That does get rid of compile errors, but it leads to the action being called during load, which causes an InvalidOperationException. – MrFox Dec 02 '13 at 13:29
  • What exactly do you want the result in the `Hidden` to be? – dav_i Dec 02 '13 at 14:12
  • Don't know, not very good with the web stuff. I ended up using a helper class that allows me to select a type for the action to perform. – MrFox Dec 02 '13 at 15:37