ASP.NET MVC routing options not working with scopes
I am currently playing around with scopes and routing within them. What I am trying to achieve is to have a url that looks like this:
PracticeAdmin/Practice/[Practice Name]
after which I could add things like Edit and Delete to the end.
I have achieved this in the past when not working with scopes by adding this annotation to the action
[Route("PracticeAdmin/Practices/{practiceName}")]
public ActionResult Details(string practiceName)
this will bring up the urls that I would like. The problem I'm running into is that when I try to do this while using scopes, I get links that look like this:
PracticeAdmin/Practices?practiceName=Practice1
which is not what I am looking for.
The code I am using to try and accomplish this with
PracticeAdminAreaRegistration.cs
using System.Web.Mvc;
namespace TrainingPortal.Areas.PracticeAdmin
{
public class PracticeAdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "PracticeAdmin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"PracticeAdmin_default",
"PracticeAdmin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new[] { "TrainingPortal.Areas.PracticeAdmin.Controllers" }
);
}
}
}
RouteConfig.cs
namespace TrainingPortal
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "TrainingPortal.Controllers" }
);
}
}
}
I named MapMvcAttributeRoutes here, which I believe should mean that routes are registered even within areas. I also tried to put the required code in PracticeAdminAreaRegistration to do the same without effect.
PracticeAdminController.cs
namespace TrainingPortal.Areas.PracticeAdmin.Controllers
{
public partial class PracticesController : Controller
{
private TpContext db = new TpContext();
// GET: PracticeAdmin/Practices
public virtual ActionResult Index()
{
return View(db.Practices.ToList());
}
[Route("PracticeAdmin/Practice/{practiceName}")]
public virtual ActionResult Details(string practiceName)
{
if (string.IsNullOrWhiteSpace(practiceName))
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Practice practice = db.Practices.FirstOrDefault(m => m.PracticeName.ToLower() == practiceName);
if (practice == null)
{
return HttpNotFound();
}
return View(practice);
}
...
Obviously this happens with other methods, but they all follow the same approach as this one.
index.cshtml snippet
@Html.ActionLink("Delete", MVC.PracticeAdmin.Practices.Delete(item.PracticeName))
@Html.ActionLink("Delete2", "Delete", new { practiceName = item.PracticeName })
As part of PracticeAdminArea / Views / Practices / Index.cshtml I tried to use both T4MVC and the normal ActionLink approach, which generate the exact same link (no surprise).
Summary
I have no idea why the Routes I have given do not show up when I try to create an ActionLink in the scope, so I was wondering if anyone could point me to how I can fix this and get the url to see how I do you like it?
source to share
After a little game, I was able to get it to work. The way I ended up fixing it was calling AreaRegistration.RegisterAllAreas()
from RouteConfig.RegisterRoutes()
after callingMapMvcAttributeRoutes()
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "TrainingPortal.Controllers" }
);
}
}
Since you are not allowed to call this method twice (or ASP.NET is very frustrated that you registered the same route names twice), I removed the call AreaRegistration.RegisterAllAreas()
from Global.asax, leaving it looking like this:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
Unfortunately this alone did not solve the problem, I also had to make a couple of changes to the controller. Change is to add attributes RoutePrefix
and RouteArea
to the controller, for example:
[RouteArea("PracticeAdmin")]
[RoutePrefix("Practice")]
public partial class PracticesController : Controller
{
This had the added benefit of specifying a route for a specific action using an attribute, Route
you no longer needed to specify these parts, so initially the action signature would look like this:
// GET: PracticeAdmin/Practices/{practiceName}/Members
[Route("PracticeAdmin/Practices/{practiceName}/Members")]
public virtual ActionResult Members(string practiceName)
{
it will now look like this:
// GET: PracticeAdmin/Practices/{practiceName}/Members
[Route("{practiceName}/Members")]
public virtual ActionResult Members(string practiceName)
{
After making all these changes, the website behaves as expected.
source to share