Search code examples
c#asp.net-mvcasp.net-mvc-routing

How can I update an area route after the website is started?


I am trying to update the route table after the application has started. I am doing this in a MVC Area called "Pages". The website has custom URLs for each customer: www.mydomain.com/Pages/CompanyName/

It works fine when you start the website. It picks up all the existing customers urls and you can navigate to them:

    public override void RegisterArea(AreaRegistrationContext context)
    {
        context.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        List<Customer> custs = new CustBLL().getActiveCustomers();

        string urlList = string.Join("|", custs.Select(c => c.UrlIdentifier).ToArray());

        context.MapRoute(
            "Pages_CUSTURL", // Route name
            "Pages/{CUSTURL}/{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
            constraints: new { CUSTURL = urlList }
        );


        context.MapRoute(
            "Pages_default",
            "Pages/{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional },
            constraints: new { controller = new ControllerConstraint() }
        );

    }

But, when a new customer is created, you can not get to the url for that customer, "www.mydomain.com/Pages/NewCompany/", because a 404 would happen.

So I tried to add a new function "UpdateRouteRegistration()" that is called after a new customer is created.

    public static void UpdateRouteRegistration()
    {
        RouteCollection routes = RouteTable.Routes;
        using (routes.GetWriteLock())
        {
            routes.Remove(RouteTable.Routes["Pages_CUSTURL"]);

            List<Customer> custs = new CustBLL().getActiveCustomers();

            string urlList = string.Join("|", custs.Select(c => c.UrlIdentifier).ToArray());

            routes.MapRoute(
                "Pages_CUSTURL", // Route name
                "Pages/{CUSTURL}/{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
                constraints: new { CUSTURL = urlList }
            );

        }
    }

The new function does not work. There are no errors when it is run but, afterwards I cannot navigate anywhere. I get a BlankView error. It seems like I corrupt the routing table.

Server Error in '/' Application.

The view 'BlankView' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Home/BlankView.aspx
~/Views/Home/BlankView.ascx
~/Views/Shared/BlankView.aspx
~/Views/Shared/BlankView.ascx
~/Views/Home/BlankView.cshtml
~/Views/Home/BlankView.vbhtml
~/Views/Shared/BlankView.cshtml
~/Views/Shared/BlankView.vbhtml
~/__MVCSITEMAPPROVIDER/BlankView.ascx

I am using MVC 5.2.3. Any help is much appreciated.

Edit: I wonder if I need to use the AreaRegistraitionContext. But from this post, I can only access it using reflection? Get Areas associated with an MVC project


Solution

  • I got this working and wanted to share with anyone who may be doing the same thing or similar. I am not sure if this is the best way but I am happy it works.

    I did two things. One, I saved the AreaRegistrationContext to a static member for later use. Two, I did not try to add/remove the existing mapping. Instead, I just added a new one.

    In my case, a new customer is not added too often so there will not be too many mappings for this area.

    Here is my final class.

    public class PagesAreaRegistration : AreaRegistration 
    {
        public override string AreaName 
        {
            get 
            {
                return "Pages";
            }
        }
    
        public static AreaRegistrationContext AreaContext { get; set; }
    
        public override void RegisterArea(AreaRegistrationContext context)
        {
    
            context.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
            // save context to static for later use
            AreaContext = context;
    
            // get customers
            List<Customer> custs = new CustBLL().getActiveCustomers();
    
            // get list of domain urls
            string urlList = string.Join("|", custs.Select(c => c.UrlIdentifier).ToArray());
    
            // map customer URLs
            context.MapRoute(
                "Pages_CUSTURL", // Route name
                "Pages/{CUSTURL}/{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
                constraints: new { CUSTURL = urlList }
            );
    
            // general area mapping
            context.MapRoute(
                "Pages_default",
                "Pages/{controller}/{action}/{id}",
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                constraints: new { controller = new ControllerConstraint() }
            );
    
        }
    
        public static void UpdateRouteRegistration(string newURLID)
        {
            // get context from static member
            RouteCollection routes = AreaContext.Routes;
    
            // get write lock to avoid threading issues
            using (routes.GetWriteLock())
            {
    
                // add new company url to route table 
                AreaContext.MapRoute(
                    "Pages_CUSTURL_" + newURLID,                                      // Route name
                    "Pages/{CUSTURL}/{controller}/{action}/{id}",                       // URL with parameters
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional },  // Parameter defaults
                    constraints: new { CUSTURL = newURLID }
                );
    
            }
    
        }
    
    
    }
    

    Good luck if you are trying to accomplish something like this with MVC area routing!