5

Consider an ASP.NET MVC application with two models: let's say Company and Person. Each company has a list of persons. Each person belongs to one company only.

If you set up the model and use Visual Studio to generate the controllers/views, you get the ability to edit the companies at /Company/{id} etc. and the ability to edit the persons at /Person/{id} etc.

But I want it to be such that you can only add a person inside a company, i.e. you would edit the persons at /Company/{id}/Persons/{id}.

How can I set up this sort of routing in ASP.NET MVC 5?


EDIT:

So I did this in my routes:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        
routes.MapRoute(
    name: "CompanyPerson",
    url: "Company/{CompanyId}/Person/{PersonId}/{action}",
    defaults: new { controller = "Person", action = "Index", PersonId = UrlParameter.Optional }
);

routes.MapRoute(
    name: "Company",
    url: "Company/{id}/{action}",
    defaults: new { controller = "Company", action = "Index", id = UrlParameter.Optional }
);

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/",
    defaults: new { controller = "Home", action = "Index" }
);

but it still isn't working. If I go to /Company/7/Person I get an index of persons, but /Company/7/Person/Create just gives the same index, and the "Create New" link points to /Person/Create instead of /Company/7/Person/Create

Is there just a way to set up all the routes explicitly, like Node or most other MVC frameworks?

Amal K
  • 4,359
  • 2
  • 22
  • 44
ian93
  • 1,488
  • 4
  • 24
  • 37

1 Answers1

6

Well, it doesn't work the way you're thinking. The reason is that you have two different controllers, Company and Person. You can't call both of them at the same time.

You are going to have to decide whether you want to edit a person at the Person controller, or edit them at the Company controller. Personally, I'd edit the person at the person level, but that will mean your Person.Edit method will have to take the companyId as well as the personId.

So you would then create a route like this:

routes.MapRoute(
    "CompanyPerson",                                // Route name
    "Company/{companyId}/Person/{personId}",        // URL with parameters
    new { controller = "Person", action = "Edit" }  // Parameter defaults
);

Then your Person.Edit method would look like this:

public ActionResult Edit(int companyId, int personId) {}

EDIT:

You are going to have to use constraints to do what you want.

routes.MapRoute(
    name: "CompanyPerson",
    url: "Company/{companyId}/Person/{personId}/{action}",
    defaults: new { controller = "Person", action = "Index", PersonId = UrlParameter.Optional },
    new {personId = @"\d+" }
);

However, this now means you can do /Company/7/Person/15/Create, and that just won't make any sense. The personId will simply get ignored.

As for why your Create Link isn't working, is because of the id being before the action. You'll also need another route.

routes.MapRoute(
    name: "CompanyPerson",
    url: "Company/{companyId}/Person/Create",
    defaults: new { controller = "Person", action = "Create" }
);

and this

@Html.ActionLink("Create", "Person", new { companyId = Model.companyId })

It would be a lot easier if you just stuck to the Id after the action. You also need to be careful of capitalizing variable names, as these are C# variables and are case sensitive.

Amal K
  • 4,359
  • 2
  • 22
  • 44
Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Thanks. That's what I was thinking of. – ian93 Jul 05 '14 at 20:03
  • Sorry. It still isn't working. Give me a minute and I'll update the question. – ian93 Jul 05 '14 at 21:23
  • @ian93 - It's not working because you completely changed it to something else. You can't make personId optional if you have something coming after it, the matching pattern won't be able to figure it out – Erik Funkenbusch Jul 05 '14 at 21:59
  • OK thanks, I didn't realize you couldn't make it optional if there was something after it. Sorry! – ian93 Jul 05 '14 at 22:29
  • @ian93 - well, you can if you use constraints to ignore the route if it's not numeric, like I did above.. but then you need a separate route to handle the Create case where no id is present. – Erik Funkenbusch Jul 05 '14 at 22:33