An Efficient Way of Handling Multiple Actions through One Route

13th June 2019

Recently, I wrote about a way that you can use one route to direct traffic to different controllers and actions in ASP.NET Core MVC.

In that example, I mentioned that there were six different types of pages that would be handled by one route.

This is great, but the problem I experienced was the data for each different types of page was housed in six different database tables.

I couldn't just potentially call up to six different database queries to try and match the type of page against the URL being pulled in. The website performance would have been compared to the speed of a slug.

Of course I could have done some funky UNION join against all six tables, but that would have still of meant a call to the database.

The solution

The idea I came up with is using a singleton within dependency injection.

For anyone that doesn't know what a singleton is, it's a class that keeps it's properties and variables set for the lifetime of the application.

So when running a website application, the singleton will be created when the website application starts, and the singleton will be destroyed once the website application stops. Each thread to the website application would be able to access the same values for each of the properties from the singleton.

The singleton would call the database to get a list of all the potential URL's on creation and when one of the types of pages is updated. This would be locked down using the "lock" keyword so no other threads can access the function whilst it's doing it's job.

The advantages of this is that the database is only involved when the website application starts, or a change has been made to one of the types of pages.

The singleton class would store a list of all the URL's and which controller and action each URL would go to. These would be stored in a ConcurrentDictionary class (rather than the Dictionary class) to support multiple users accessing it at the same time. This dictionary is effectively "the cache".

The "key" property of the ConcurrentDictionary would be the URL hashed into a number (the GetHashedCode function that appears on all objects). I did this to speed up the lookup of the URL.

As an addition, when a user updates one of the types of pages, it fires a new thread to update the URL's. This means that the user who has updated the page doesn't have to hang around for the background service to complete it's job in updating the URL's.

An example on how to do this is below:

// BackgroundService.cs
using System.Collections.Concurrent;

namespace MyProgram.Routes
    public partial class BackgroundService : IBackgroundService
        protected ConcurrentDictionary<int, TypeRoute> _types;

        public BackgroundService()

        public virtual TypeRoute GetTypeRoute(string url)
            // Use the URL of the page to get the appropriate "type" record
            return _types.ContainsKey(url.GetHashCode()) ? _types[url.GetHashCode()] : null;

        public virtual void UpdateURLs()
            lock (this)
                // Lock so no other thread can access whilst updating the URL's

                // Use your DB query to get the different types

                // Create new variable of "types" and store it against the class specific variable once all the data has been added.
                var types = new ConcurrentDictionary<int, TypeRoute>();
                types.AddOrUpdate(("type1").GetHashCode(), new TypeRoute { Url = "type1", Controller = "Type", Action = "Type1" }, (k2, l) => new TypeRoute { Url = "type1", Controller = "Type", Action = "Type1" });

                _types = types;

    public partial interface IBackgroundService
        TypeRoute GetTypeRoute(string url);

        void UpdateURLs();

    public partial class TypeRoute
        public virtual string Url { get; set; }
        public virtual string Controller { get; set; }
        public virtual string Action { get; set; }

// Startup.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace MyProgram
    public class Startup
        public Startup(IConfiguration configuration)
            Configuration = configuration;

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)


            // Add singleton service to dependency injection
            services.AddSingleton<IBackgroundService, BackgroundService>();


// TypeInsertUpdate.cs
using System.Threading;
using System.Threading.Tasks;

namespace MyProgram.Routes
    public class TypeInsertUpdate
        protected readonly IBackgroundService _backgroundService;

        public TypeInsertUpdate(IBackgroundService backgroundService)
            _backgroundService = backgroundService;

        public virtual void Update()
            // Do update task
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken = cancellationTokenSource.Token;

            Task.Factory.StartNew(() =>
                // Async thread meaning the user does not need to hang around to finish.

            }, cancellationToken);

Want More ASP.NET Core Coding Tutorials?

Subscribe to our YouTube channel to get more ASP.NET Core coding tutorials.

You'll get videos where I share my screen and implement a how-to guide on a topic related to ASP.NET Core.

You can expect to see videos from the following technologies:

  • Blazor
  • Web APIs
  • SQL Server
  • Entity Framework
  • SignalR
  • and many more...

By subscribing, you can get access to all my ASP.NET Core coding tutorials completely free!

And so you never miss out on a new video, you have the option to be notified every time a new video is published.

So what are you waiting for?

Click here to subscribe to our YouTube channel.

David Grace

David Grace

I am a .NET developer, building web applications in .NET Framework and .NET Core with a SQL Server database.

Some of the .NET packages I have used include Entity Framework and MVC.

I've also used many JavaScript frameworks such as React and jQuery, and have experience building CSS in SASS.

Twitter Feed