ASP.NET MVC Hosting: Optimizing Your ASP.NET Core Application

If you aren’t using all six of these tips for speeding up your ASP.NET Core application, your application isn’t running as fast as it could. And implementing any of these will speed up your app enough that you users will notice.

The two slowest things you can do in data processing are:

  • Issue a call to another computer, especially if you’re using HTTP, the world’s slowest protocol
  • Performing disk I/O

Those are major problems in a business-oriented ASP.NET Core application which is a) based on receiving HTTP calls over the internet, and b) constantly triggering disk I/O by making database requests.

And then it gets worse: As an ASP.NET Core developer, you have no control over either network latency and very little control over database response times. Which mean that, in an ASP.NET Core application, there are two levers you can pull to get an “Oh, that was faster!” response from your users:

  • Faster responses from the web server when the browser requests a new page
  • Faster load times when that page arrives at the browser

And you want to pull both of those levers because there’s very little that users like better than a fast, responsive UI. So here are six tips (and a bonus) to deliver a faster UI for your users.

Tip 1: Don’t Configure More Than You Need

First things first: In your Program.cs file, you should take advantage of ASP.NET Core’s configuration options to slim down your application and only load only the components that your application needs. That lets you start with a lightweight, nimbler application.

Tip 2: Skip Going to the Database

If you can eliminate trips to the database server, you can cut out one of those “two slowest things in data processing.” You can eliminate trips to your database server by using .NET’s MemoryCache object to cache, in memory, the 20% of the data that handles 80% of the requests to your application. The trick here is that cached data does not reflect changes made back at the database after the data was retrieved into the cache, so you have to deal with your cached data going stale.

The simplest way to implement caching is to put all your data access code in methods in a repository class and then have your application call those repository methods when it needs data. In those repository methods, when you fetch any data, add it to the MemoryCache so that the next request doesn’t have to go to the database server.

You have three choices for handling stale data in the cache:

  1. Choose data for your cache that doesn’t change often or changes outside business hours.
  2. Cache the data with some specified timeout, after which the data will be purged from the cache. Purging the data by the end of business day means that, tomorrow, the first request from the application will pick up any data updated overnight.
  3. Add an optional Boolean parameter to your method that triggers clearing the cache. When the cache is empty, your code makes that trip to the database to get the latest data (and refreshes the cache for subsequent requests). Applications can use that parameter when they absolutely must have the latest data.

Here’s a typical example with a controller calling the repository:

public class HomeController
{
  CustomerRepository custRepo = null;
 
 public Home(IMemoryCache cache)
  {   
     custRepo = new CustomerRepository(cache);
  }
 
  public IActionResult Index(string cId, bool force = false)
  {
     Customer cust = custRepo.GetCustomerById(cid, force)
     return View(cust);
  }
}

public class CustomerRepository
{
  private IMemoryCache cache;
  
  public CustomerRepository(IMemoryCache cache)
  {
       this.cache = cache;
  }
  public async Customer GetCustomerById(string cId, bool force = false)
  {
     if (force)
     {
        cache.Remove(cId);
     }
    Customer cust = await cache.GetOrCreateAsync<Customer>(
          cId,
          async e => {
                                    e.cacheEntry.AbsoluteExpirationRelativeToNow = 
                                                                                       TimeSpan.FromMinutes(30);
                                   return await GetCustomerAsync(cId);
                                }
        );
  return cust;
}
…

C#

If you’re working in a server farm, you don’t want to cache data on individual servers (that can lead to inconsistency if one server in the farm has cached a version of the item that has different values than an updated version of that item on another server in the farm).

Instead, on a server farm, take advantage of ASP.NET Core support for a variety of distributed caching solutions, including Redis, NCache and SQL Server (which uses in-memory OLTP tables to speed data access).

Tip 3: Don’t Send Any More Than You Need to the Client

Be judicious about passing data to the client: The more data you send to the client when a page is first requested, the slower that page’s initial display is going to be. Specifically, if you’ve retrieved a big list of data that you will be displayed in a grid, don’t display all the rows at once. In fact, don’t download any data until after the page displays—that way the user is kept busy reading the page and doesn’t mind (as much) that the data is still on its way.

When you do deliver the data, download just enough so that your user satisfied with the initial display of your grid. There are two scenarios here:

  • If you’re implementing paging, download just enough data for the first page.
  • If you’re using a grid with “infinite scroll,” download just enough data to fill the webpage (and a little more).

Tip 4: Download What You Need from Closer to the Browser

If the CSS and JavaScript files your page is using are industry standard technology (e.g., Bootstrap for CSS, React or Angular) then get those files from one of the designated Content Delivery Network (CDN) sites. While a CDN is accessed through a single URL, the nodes supporting that URL are scattered across the world. Unless your clients are concentrated near your data center, it’s pretty much certain that your users are closer to a CDN node than they are to your data server.

When you fetch from a CDN, the worst case is that user’s local CDN node doesn’t have a copy of the requested file (unlikely, but possible). If so, the user’s request will populate the CDN node so that any subsequent request from that user (or any other user local to the CDN) will fetch the file from that node. Plus, if the user’s browser has already loaded that industry-standard file from the CDN during a visit to some other site, your user’s browser will just keep using that downloaded version rather than fetch the file again.

You can also consider moving your own custom files out to a CDN (for example, the Azure Content Delivery Network). After all, does your user in Singapore really need to reach all the way across the world to your server in New Jersey just to download your company logo?

Tip 5: Download Smaller and Fewer Files

Even after moving to a CDN for your industry-standard files, your application may still include a lot of your own JavaScript and CSS files.

You should be minifying those files to eliminate what isn’t required on the browser (spaces, carriage returns, indenting, meaningful variable names—everything that makes your file content readable). Slimming down your files speeds downloads and gets your page up faster.

And, because modern browsers support the GZIP protocol, you should also be zipping your CSS and JavaScript files up into smaller, compressed bundles that will download faster, speeding up your initial page display.

There’s another benefit, besides smaller files, here: In HTTP 1.1, your browser is limited to seven simultaneous requests. Bundling up your CSS files means just two requests (one for your CSS bundle and one for your JavaScript bundle) gets your browser all your CSS and JavaScript files.

The browser will unpack those bundles as they are received and that will eat up some of the time you’ve saved in downloading fewer, smaller files—but you’ll probably still come out ahead. Of course, you’ll have to update your <link> and <script> elements to point to your zipped up files so that the browser uses them.

Obviously, whatever the benefits of bundling and minifying are, there’s some extra work here that you’d prefer not to do by hand. Fortunately, ASP.NET Core is compatible with WebOptimizer, which will both minify and bundle your CSS and JavaScript files at runtime.

Tip 6: Call the Server Less

When you do return a page from your ASP.NET Core application, take advantage of the ResponseCache attribute, especially for frequently requested pages—that avoids having the browser make a trip to your web server.

The ResponseCache attribute, applied to an action method, directs clients and proxies to hold on to pages returned by your application. The browser will then satisfy subsequent requests for those pages from those closer, saved results rather than sending another request your more distant server. You not only give your users a faster response, you reduce the demand on your web server so you can handle more requests, faster.

Have questions? Our consultant is just a WhatsApp message away – reach out for personalized support! @ +92 313-325 8907

Leave a Reply

Your email address will not be published. Required fields are marked *