views:

90

answers:

1

I have an MVC action on my abstract BaseControlller like this (and this action is common across all inheriting controllers):

    //
    // GET: /controller/RenderForm/{formType}
    [Authorize(Roles = "Administrators")]
    public ActionResult RenderForm(FormType formType, BaseContentObject contentObject)
    {
        string constructPartialViewName = RouteData.Values["controller"] + "-" + formType + "Form";
        return PartialView(constructPartialViewName, contentObject);
    }

with this route:

        routes.MapRoute(
            "ContentObjectsFormRendering",
            RouteType.Regular,
            "{controller}/RenderForm/{formType}",
            new {controller = "", action = "RenderForm", formType = ""}, null
            );

The problem comes in when I make a browser request like:

~/Page/RenderForm/Create

This is a request to render a Create form. This is done from the browser - for instance, typing in the URL. There is no BaseContentObject instance here (please note that this can never be provided thru browser for Create as it is always pulled out of datastore in case of editing) and I think this is what is bothering him.

(The exception is: Cannot create an abstract class.)

But when performing RenderAction like this:

    <%
        Html.RenderAction("RenderForm", ViewData.Model.ContentType.ToString(), new {formType = FormType.Edit, contentObject = ViewData.Model}); %>

it renders the Edit form with details from BaseContentObject instance (provided thru ViewData) just fine.

If I remove the BaseContentObject contentObject parameter from the action then rendering Create works fine but then I cannot provide the content object param in the second case for Edit.

How can I solve this?

+1  A: 

Since your RenderForm is common (in base controller) then MVC uses this method's signature to instantiate paramters. This goes like this:

  • action is RenderForm(FormType formType, BaseContentObject contentObject)
  • there's form data like "contentObject.Property" thus MVC needs to instantiate contentObject
  • a type for contentObject is BaseContentObject
  • BaseContentObject cannot be instantiated since it's abstract

Your problem is that you think that if you override RenderForm with different/specific contentObject parameter type then the new parameter type will be used - but from what I see here this is wrong. The base method signature will be used.

Solutions:

  • override DefaultModelBinder and tweak instantiation there; I'm not sure if you can detect real action/parameter type, though;
  • don't use common base RenderForm action; extract required common functionality into protected base method (e.g. DoRenderForm) and call it from real actions.
queen3
I have solved this by replacing BaseContentObject parameter with a content slug parameter of type string and then pull the object from the datastore using this string or if it is null or empty, I just redirect to Create view. You were right, the problem is in abstract class being used as a method parameter.
mare