0

Consider the following two Models: AModel (Class A) and BModel (Class B), and two partial views: AView (bound to AModel) and BView (bound to BModel).

I have a Controller called TestController. In TestController I have the following methods:

<HttpPost>
Function Index(model as AModel) As ActionResult
    Return View(model)
End Function

<HttpPost>
Function Index(model as BModel) As ActionResult
    Return View(model)
End Function

In Index I have the following code:

@Using Html.BeginForm()
@<div>
    @Html.Partial("~/Views/Test/AView.vbhtml")  ' this is here for test only
    <div><input type="submit" name="submit" value="Submit" /></div>
</div>
End Using

My partial views look like this:

@ModelType Company.Domain.Models.ModelA '(or ModelB for the other)

<div>@Html.LabelFor(Function(m) m.Name)</div>
<div>@Html.TextBoxFor(Function(m) m.Name)</div>

... and so on, several Input fields follow ...

When I fill in and submit AView I get the following error:

The current request for action 'Index' on controller type 'TestController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Index(Company.Domain.Models.AModel) on type Company.WebUI.Controllers.TestController
System.Web.Mvc.ActionResult Index(Company.Domain.Models.BModel) on type Company.WebUI.Controllers.TestController

After some research I tried putting <ActionName("Index")> over each Index method in TestController but generates the same error (I changed the method names to AIndex and BIndex then placed the action name attribute above them and tested.)

What I'm trying to achieve: I have two forms I need my user to fill out (one after the other). Each form has it's own set of properties and validation (naturally), hence AModel and BModel. I created two matching Partial Views (partial because I will want to use those views within other main views later on).

I'm lost as to how to achieve what I want here because ASP.NET isn't recognizing the different method signatures, nor am I sure I'm going about doing this correctly under ASP.NET MVC anyway. Under a competing platform I would simply #INCLUDE my pages as needed.

I hope my explanation is clear, appreciate any advice.

2

3 Answers 3

3

Simply put, you can't do this. MVC requires that each action method be unique, and not overloaded for each HTTP verb (get, post, etc.). What this means is that you can only have one Post method for Index.

To understand why this is, you have to understand how model binding works. When you post to data to the client, the framework does not yet know what types are being passed to it. It only has key/value pairs, which are all text.

MVC looks up the action name, looks for either a Post method (or if not present a method with no verb at all) and selects it. Note, it still doesn't know what type parameters have been posted. To determine the type of the parameters, MVC then looks at the parameters of the action method, then it tries to match the keys in the posted data to the properties of the methods parameters.

In other words, MVC doesn't know what type to pick.. so it can only demand there be a single method in which parameters are then matched against the posted data. If you have more than one Index method with the same verb, then MVC doesn't know which one to use, and since it doesn't know the types of the data posted... all it can do is throw it's hands up and say the situation is ambiguous.

Sign up to request clarification or add additional context in comments.

1 Comment

This clarifies greatly, I'll mark as answer and pursue the other comments (recommendations).
2

You need to give you POST methods different names (the client does not know which one i should be posting back to)

Function IndexA(model as AModel) As ActionResult {...

Function IndexB(model as BModel) As ActionResult {...

and in the view

@Using Html.BeginForm("IndexA", "Test")
.... // first model
@Using Html.BeginForm("IndexB", "Test")
.... // second model

4 Comments

You can still put you partials in the forms. The key issue is that the client currently posts back to Index() but you have 2 POST methods called Index so the error occurs (the different signature is irrelevant because the client has no concept of a model, just controls representing the properties of the model)
Right. But I'm trying to have a master page with partial views which are the forms themselves. The idea being that I can include those forms elsewhere if need-be but also without exposing the "path" to forms, so companyname.com/application (when the user presses Submit, the controller(s) process it and inject a new form (or the next form), the user still see's /www.companyname.com/application and not www.companyname.com/application/form2 (or IndexB or whatever). Is this possible or is it best to just create unique forms (views, non-partial)?
The GET method can still be called Index() so you will see www.companyname.com/application (assuming your default route is /Test/Index)
+2 found your comments helpful.
0
public class MyModelView
{
        public MyModelAView MyModA {get;set;}
        public MyModelBView MyModB {get;set;}
        public MyModelView()
        {
             MyModA = new MyMoModelAView();
             MyModB = new MyMoModelBView();
        }
}

and in your Controller:

public ActionResult Index()
{
     MyModelView model = new MyModelView();
     return View("Index", model: model); // model: model <- syntax from c#, it mean that for parameter, which name "model" pull value: `MyModeLView model`
}

your View model from Index:

@model MyModelView //remember about namespace

and now create Two PartialView, which contain:

PartialView, name: "_LoginModelA", model: MyModelAView:

@using MyModelAView
@Html.BeginForm("IndexA","Test", FormMethod.Post, null)
{
    //implement your model
}

PartialView, name: "_LoginModelB", model: MyModelBView:

@using MyModelBView
@Html.BeginForm("IndexB","Test", FormMethod.Post, null)
{
    //implement your model
}

and in your ViewModel (main, which contain model: MyModelView)

@Html.PartialView("_LoginModelA", Model.MyModA)

@Html.PartialView("_LoginModelB", Model.MyModB)

in your controller add two method:

[HttpPost]
public ActionResult IndexA(MyModelAView model);

[HttpPost]
public ActionResult IndexB(MyModelBView model);

It should help you, but if you don't understand my way, please answer :-). Good day.

4 Comments

I was able to follow your explanation very well, but it doesn't work. There are two problems, 1) on post-back the fields become empty and 2) I still get the ../IndexA in the URL. It doesn't seem possible to do what I want with MVC. In Java for example, I can simply include a view, pass it my vars (model) and have it presented regardless of URL, same with PHP and pretty much all other frameworks I've played with. ASP.NET MVC however expects things to be wired very specifically to work (at least according to convention and by design).
Ok, i'm not glad. maybe change MyModelAView in IndexA for FormCollection and then debug it (you can simple use Immediate Window in VS). Could you give me the result?
I did try a few things, but still -- nothing. I think I'm going to surrender to the MVC way and instead of partial views, just views and work some session magic to make it work and safe. Appreciate the effort !
Haha, I've been spending days on this one problem, at a certain point my brother you just have to. Wink.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.