Introduction
Performance is the main concern when thinking about the success of any website. We have thousands of websites available which perform similar tasks but not all are popular. Among these thousands of website, some are popular and those are the ones that have highly optimized data, security and well performing. We always prefer to use those websites which are frequently accessible in minimum time.
Let’s take one example, we have multiple search engines available like Google, Bing, Baidu, Ask.com and many more. But you all know that we prefer GOOGLE and BING, because these are accessible within 3-4 seconds with search results. Let's suppose if these take more than 10 seconds to search the results, then do you think that we will go with these. I don’t think so. We generally don’t want to wait for the results. We always want the results in minimum time without wasting much time. So, as per the above discussion, we can say performance is the main concern of any website. If your website is not performing well, then the user will have minimum interaction with your website.
Background
Today, we will learn a few points which can be implemented while developing a website in ASP.NET Core to improve performance.
ASP.NET Core is a free, open-source and cross-platform web development framework which was created by Microsoft. It is not an upgraded version of ASP.NET, but it is completely rewritten from scratch which comes with a single programming model of ASP.NET MVC and ASP.NET Web API.
Here, I am not going to discuss the features of ASP.NET Core. You can follow some of the most popular articles which help you to understand the ASP.NET Core. Let's start the tips for performance improvement of ASP.NET Core application.
1. Always Use ASP.NET Core Latest Version
The first version of ASP.NET Core was released in 2016 with Visual Studio 2015 and now we have ASP.NET Core 2.2 version available. If you compare the performance improvement from the first version to the current version, you will find that the application developed in ASP.NET Core 2.2 (current latest version) runs very fast compared to the previous version. Microsoft always brings some performance improvement in the next version compared to the previous version.
So, while creating the application using ASP.NET Core, always prefer to use the latest version of ASP.NET Core. With the latest version, you will definitely find the performance improvement. Next version of ASP.NET Core is 3.0 which is under development and hopefully, it will be released very soon with Visual Studio 2019.
2. Avoid Synchronous Call in any Level
While developing the ASP.NET Core application, just try to avoid creating blocking calls. Blocking call means a call that blocks the next execution until it will not be completed. Blocking call or synchronous call can be anything, either you are fetching the data from API or performing some operations internally. You should always execute the call in an asynchronous manner.
3. Always Use Asynchronous Programming (Async-Await)
Asynchronous programming model was introduced in C# 5.0 and became very popular. ASP.NET Core uses the same Asynchronous Programming paradigm to make an application more reliable, faster and responsiveness.
You should use end to end Asynchronous Programming while writing the code. Let’s take one example, we have one ASP.NET Core MVC application with database implementation. As we know, it could have many separations and it all depends on user project architecture, but take some simple example where we have Controller > Repository Layer, etc. Let's see how you will write the sample code at the controller level.
[HttpGet]
[Route("GetPosts")]
public async Task < IActionResult > GetPosts()
{
try {
var posts = await postRepository.GetPosts();
if (posts == null) {
return NotFound();
}
return Ok(posts);
}
catch (Exception) {
return BadRequest();
}
}
This is how we implement asynchronous programming at the repository level.
public async Task < List < PostViewModel >> GetPosts()
{
if (db != null) {
return await(from p in db.Post
from c in db.Category
where p.CategoryId == c.Id
select new PostViewModel
{
PostId = p.PostId,
Title = p.Title,
Description = p.Description,
CategoryId = p.CategoryId,
CategoryName = c.Name,
CreatedDate = p.CreatedDate
}).ToListAsync();
}
return null;
}
4. Avoid Task.Wait or Task.Result with Asynchronous Programming
While working with Asynchronous Programming, we recommended you avoid using Task.Wait
and Task.Result
and try to use await
because of the following reason:
- These block the thread until the task completed and wait for completion of the task. Waitblock the thread synchronously until the task completes.
Wait
and Task.Result
both wrap any type of exception in AggregateException
and create the complexity while doing exception handling. If you use the await
instead of Task.Wait
and Task.Result
, then you don’t have to worry about exception handling. - Sometimes, these both block the current thread and create
DEADLOCKS
. Wait
and Task.Result
can only be used if parallel task execution is going on. We recommend that don’t use it with Async programming.
Let's understand a good and bad example of Task.Wait
as follows:
Task task = DoWork();
await task;
Task task = DoWork();
task.Wait();
Let's understand a good and bad example of Task.Result
as follows:
Task < string > task = GetEmployeeName();
txtEmployeeName.Text = await task;
Task < string > task = GetEmployeeName();
txtEmployeeName.Text = task.Result;
Know more about Best Practices for Asynchronous Programming.
5. Perform I/O Operations Asynchronously
While performing I/O operations, you should perform it asynchronously without affecting the other processes. I/O operations mean, doing some execution with a file like uploading or retrieving files. It could be anything like image uploading, file uploading or anything else. If you try to accomplish it in a synchronous manner, then it blocks the main thread and stops the other background execution till the I/O completes. So, from the performance point of view, you should always use the Asynchronous execution for I/O operations.
We have lots of Async
methods available for I/O operations like ReadAsync
, WriteAsync
, FlushAysnc
, etc. Here is one simple example how we can create a copy of one file asynchronously.
public async void CreateCopyOfFile()
{
string dir = @"c:\Mukesh\files\";
using(StreamReader objStreamReader = File.OpenText(dir + "test.txt"))
{
using(StreamWriter objStreamWriter = File.CreateText(dir + "copy_test.txt"))
{
await CopyFileToTarget(objStreamReader, objStreamWriter);
}
}
}
public async Task CopyFileToTarget(StreamReader objStreamReader, StreamWriter objStreamWriter)
{
int num;
char[] buffer = new char[0x1000];
while ((num = await objStreamReader.ReadAsync(buffer, 0, buffer.Length)) != 0) {
await objStreamWriter.WriteAsync(buffer, 0, num);
}
}
6. Always Use Cache
We can increase the performance of the application if we can reduce the number of the request to the server every time. It does not mean that you will not make the call to the server, it only means that you will not make the call to server every time. First time, you will make the call to the server and get the response and this response is stored somewhere for future perspective for some time (there will be some expiry) and next time, when you will make the call for the same response, then first, you will check that if you have already got data in the first request and stored somewhere and if it is yes, then you will use the stored data rather than making the call to the server.
Keeping the data in that location from where we can get it frequently without making the call to the server is a good practice. Here, we can use the Cache
. Caching the content helps us to reduce server calls again and again and it helps us to increase the performance of the application. We can do the cache at any point of a location like on client-side caching, server-side caching or client/server-side caching.
We have different kinds of caching available in ASP.NET Core like we can do the caching In-Memory or we can use Response caching or we can use Distributed Caching. More about Caching in Asp.Net Core.
public async Task < IActionResult > GetCacheData()
{
var cacheEntry = await
_cache.GetOrCreateAsync(CacheKeys.Entry, entry => {
entry.SlidingExpiration = TimeSpan.FromSeconds(120);
return Task.FromResult(DateTime.Now);
});
return View("Cache", cacheEntry);
}
7. Optimize Data Access
We can also improve the performance of the application to optimize the data access logic. As we all know, most of the application is totally dependent on the database and every time, we have to fetch the data from a database and need to show on UI. If it is time-consuming, then the application will take much time to load. Here, we have a few techniques which can be implemented while doing the code which can improve the performance much better.
- Reduce the number of HTTP calls, means you should always try to reduce the number of network round trips.
- Try to get all the data in one go. Means rather than making multiple calls to the server, just make one or two calls which can bring all required data.
- Frequently, set the cache on data which is not being changed.
- Don’t try to get the data in advance which is not required, it will increase the load on the response and your application will load slower.
8. Optimize Custom Code
Apart from optimizing the data access logic, we can also optimize the business logic or middleware custom code. Optimizing or refactoring these codes help us to improve the performance of the applications. Here, we have some points on which we should focus.
- Custom Logging, Authentication or some custom handler code which executes on every request should be optimized.
- Don’t perform custom long-running custom execution in the business logic layer or middleware, it basically blocks the request to go to the server and application takes much time to get the data. You should have optimized code for this, either at the client side or database side.
- Always check that a long-running task should be performed asynchronously without affecting other processes.
- You can take real-time client-server communication example as SignalR which works asynchronously.
9. Entity Framework Core Query Optimization
As we all know, EF Core is an ORM for .NET developers which helps us to play with database object without writing much code as usual. It helps us to use a database using Models. Data access logic code can play a vital role in performances. If your code is not optimized, then your application will not perform well.
But if you write your data access logic in optimize ways in EF Core, then definitely it improves the performance of the application. Here, we have some of the techniques which will increase the performance.
- Use No Tracking while getting the data which is the only read-only purpose. It improves performance.
- Try to filter the data on the database side, don’t fetch the whole data using query and then filter at your end. You can use some function available in EF Core like
Where
, Select
which help you to filter the data on the database side. - Retrieve only the required number of records which is necessary using the help of
Take
and Skip
. You can take one example of paging where you can implement Take
and Skip
while clicking on the page number.
Let's take one example and try to understand how we can optimize the EF Core query using Select
and AsNoTracking
.
public async Task < PaginatedList < Post >> GetPagedPendingPosts
(int pageIndex, int pageSize, List<Category> allowedCategories)
{
var allowedCatIds = allowedCategories.Select(x => x.Id);
var query = _context.Post
.Include(x => x.Topic.Category)
.Include(x => x.User)
.Where(x => x.Pending == true && allowedCatIds.Contains(x.Topic.Category.Id))
.OrderBy(x => x.DateCreated);
return await PaginatedList<Post>.CreateAsync(query.AsNoTracking(), pageIndex, pageSize);
}
10. Some Other Tips
Here, we have some other performance improvement stuff which can be implemented in ASP.NET Core application.
- Write logic in such a way for the process which is optimized and tested and which minimizes the exception to be a part of the flow. Less number of chances to execute the exception code means high performed application.
- Always try to use simple NET code or highly performed ORM like Dapper for database operations. It is because if database operations will take much time to execute the query, then it will decrease the performance of the application. As we all know, in the development phase, implementing the ADO.NET code and managing it will take more time as compared to ORM implementation. But this is also true if you use simple ADO.NET, then your application will be faster than if you use any other ORM.
- If response size is large, than obviously it will take much time to load it. So, always reduce the response size using compression techniques and then load the data. You can implement the compression logic in middleware. We have some of the compression providers like Gzip, Brotli.
public void ConfigureServices(IServiceCollection services)
{
services.AddResponseCompression();
services.Configure<GzipCompressionProviderOptions>(options => {
options.Level = CompressionLevel.Fastest;
});
}
Bonus Tips (Client Side)
We would like to share some of the performance tips for client-side code. If you are creating a website using the ASP.NET Core MVC, then I believe you should always follow these tips to make your website perform well.
- Bundling and Minification
Using bundling and minification, we can optimize the content before loading on UI. From the performance point of view, always try to load all your client-side assets like JS/CSS in one go. It reduces the number of network hits. You can first minify your files using minification and then bundle those in one file which can be loaded faster and reduce the number of an HTTP request for resources.
- Load JavaScript at Last
You should always try to load your JavaScript files at last. We have many benefits like first of all, your website gets rendered in minimum time and when your JavaScript will execute, then your DOM elements will be available. Using this, you can make your application faster.
- Use CDN
If you have your custom CSS or JavaScript files, then it’s OK to load it. But if you are using some third party libraries which have CDN version available, then always try to use the CDN file path rather than downloaded library file path from your end. It is because CDN has different servers available on a different zone, if you are using a website in one zone, then you will get the CDN file from your zone server when you hit the website, which is very fast.
Conclusion
So, today, we have learned how to improve the performance of an ASP.NET Core application.
I hope this post will help you. Please give feedback below using comments which will help me to improve myself for the next post. If you have any doubts, please ask them in the comment section and if you like this post, please share it with your friends. Thanks!