Telerik blogs

When developing a new application for the web, some precautions deserve attention, such as user experience, performance and security, among others. Check out this blog post from the ASP.NET Core Basics series for a collection of good practices to review when you start a new web project.

To create a new web application, it is not only necessary to meet technical specifications with the business rules. Much more, there are factors that, if not considered, can compromise the entire operation of the application.

That’s why the purpose of this post is to bring up a set of best practices essential for a web application's success in ASP.NET Core.

Why Should We Care About Best Practices?

Although most developers know or have heard about best practices, the truth is that they don’t always consider them when creating a new application.

The reasons for not considering best practices range from the programmer’s lack of knowledge to the lack of time to think of smarter and safer solutions to solve a problem or create a new function.

Whatever the reason, we must keep in mind that there should always be a place to utilize best practices, as the success or failure of an application depends a lot on how it was built.

Imagine the following scenario: As a developer, you are given the task of creating a registration system that has many functions and must have a login screen with authentication, but initially few users will have access to the system.

As your deadline is short, you decide to create a simple and not very secure authentication system—after all, only a few users will be allowed to access it. But imagine that after a few months, the system grows quickly and hundreds or thousands of users are now accessing it. What would happen with the fragile authentication system you created if an unethical hacker invaded it and proceeded to capture the data of hundreds or thousands of users?

Best practices are certainly the prime solution to avoid this and other catastrophes.

Best Practices for Creating a New ASP.NET Core Project

When creating a new project, we must always consider the use of best practices. Even if it takes more time to choose which is the best approach to develop a given function, it is still worth it. Best practices avoid several future problems that, in addition to consuming more time for resolution, may endanger the correct functioning of the entire program.

Below is a list of best practices that you can always consider when creating a new ASP.NET Core application.

Always Consider Using Async Methods

Unlike synchronous methods, asynchronous methods allow a certain action to be started without the previous one has finished. In this way, multiple functions can be executed simultaneously and finished according to the time each one needs.

Imagine the following scenario: You are going to pay on an online shopping website with a credit card. When you click on “make payment,” you usually receive a notification that your payment is being processed, and depending on how it happens, you will receive a notification indicating the success or error generated. In the meantime, you are free to browse the website or do whatever else, since it uses an asynchronous method.

But if we were to use the synchronous approach, when clicking on “make payment,” you would need to wait for the entire process of card authentication and payment gateway confirmation, which could take a considerable time, blocking any other action.

Below are two examples. The first shows a synchronous endpoint, and the second shows how the same endpoint can be modified to work asynchronously, where the word await is used before calling the function that has “Async” in the name. In this example, we are using EF Core’s native async function, but you can create your own async functions.

app.MapGet("/books/id", (ApplicationDBContext db, Guid id) =>
{
	var book = db.Books.FirstOrDefault(b => b.Id == id);

	return book is not null ? Results.Ok(book) : Results.NotFound();
})
.WithName("GetBooksById")
.WithOpenApi();
  • Asynchronous endpoint
app.MapGet("/books/id", async (ApplicationDBContext db, Guid id) =>
{
	var book = await db.Books.FirstOrDefaultAsync(b => b.Id == id);

	return book is not null ? Results.Ok(book) : Results.NotFound();
})
.WithName("GetBooksById")
.WithOpenApi();

👉 If you want to know more about (a)synchronicity in .NET, I suggest reading this article: Making an Asynchronous Breakfast in .NET.

Use Logs Correctly

Logs are important in any web application because through them it is possible to record the events that occurred during the execution of a program.

However, if the logs are not used responsibly, the proper functioning of the application may be compromised. After all, the logs may not be registered correctly, making it difficult to identify a problem, and the exaggerated use of logs can also become a bottleneck for the application.

Following are some tips for the good use of logs.

Log Levels

Log levels are used to identify which category best fits the log record. Below is an example of each of the levels.

//Trace
_logger.LogTrace($"Processing request from the user: {userName}");

//Debug
_logger.LogDebug($"The zip code is default for the user: {userName}");

//Information
_logger.LogInformation($"Starting execution...");

//Warning
_logger.LogWarning($"User:{userName} without address");

//Error
_logger.LogError($"Username property cannot be null");

//Critical
_logger.LogCritical(ex.Message, "An error occurred in our systems. Please contact immediately the support team!");

Consider Using Third-Party Libraries

Although .NET already has a logging system natively, there are tools that can be of great help due to its various possibilities, one of which is Serilog.

Serilog provides diagnostic logging for files, consoles and even more places is easy to configure, and it has a wide variety of functions for modern applications.

Use Telerik REPL to Test and Share Code

Progress Telerik REPL for ASP.NET Core is a playground where you can test all your ideas, as well as learn and develop cool tricks.

Use the REPL to share code snippets, edit demos in place, play around with existing components, showcase your work and save time with simple coding. Plus, it is free to use!

Write Clean Code

Writing clean code allows program maintenance to be done efficiently by reducing errors and increasing application quality.

Below is a list of sample code snippets showing first the way to be avoided and then the way to be followed.

Switch Case

  • Good
	switch (item.Name)
	{
		case "Rin Tin Tin":
			typeOfAnimal = "dog";
			break;
		case "Mr. Bigglesworth":
			typeOfAnimal = "cat";
			break;

		case "K-9":
			typeOfAnimal = "fish";
			break;
		default:
			typeOfAnimal = "undefined";
			break;
}
  • Better
typeOfAnimal = item.Name switch
	{
		"Rin Tin Tin" => "dog",
		"Mr. Bigglesworth" => "cat",
		"K-9" => "fish",
		_ => "undefined",
	};

👉 You can access and test the REPL code snippet here: Good switch case.

Using Conditional Operator “?” (Ternary) to Null Value Verification

The use of the ternary operator to check for nulls is an intelligent solution that reduces several lines of code as can be seen in the example below:

  • Avoid
public static string CheckPetnameWrong(string petName)
{
		string defaultPetName = "Bingo";
		
		if (!string.IsNullOrEmpty(petName))
	{
		return petName;
	}
	else
	{
		return defaultPetName;
	}
}
  • Use
public static string CheckPetnameCorrect(string petName) => !string.IsNullOrEmpty(petName) ? petName : "Bingo";

👉 You can access and test the REPL code snippet here: Using Conditional Operator.

Save Time with Ready-Made Components

Developing components such as data grids, data tables and pagination, among others, can take a lot of effort if you’re going to create them from scratch, let alone creating the HTML, CSS, JavaScript and even the backend to make them functional.

Fortunately, there are platforms that already have these and many other components ready to use, and certainly the most suitable is Telerik UI for ASP.NET Core.

See below an example of a Data Grid made with Telerik UI for ASP.NET Core. Using just a few lines, you have a complete Data Grid with pagination, editing and deleting functions.

👉 You can try it out and get the source code here at the REPL Data Grid.

data grid

Ensure App Security

Security is one of the most important topics when creating a new application because all the work done can be compromised if it is fragile.

Currently, JWT (JSON Web Tokens) is one of the most known and trusted methods to implement an authentication and authorization system.

JWT is an open standard that allows you to securely pass data between a client and server in the form of a JSON object.

Despite being a simple system, implementing authentication and authorization with JWT in ASP.NET Core is a bit complex, but you can follow this guide—Authorization and Authentication in Minimal APIs—that teaches you how to implement JWT from scratch in a Minimal API.

Always Create Unit and Integration Tests

Testing an application is essential to ensure that it will correctly perform the functions for which it was built.

To ensure this, there are unit and integration tests, which can be developed before, during or after the creation of the program.

Unit tests simulate the execution of a determining process unit. For example, in a function that checks if a user’s phone number is valid, we can create a test that uses a fake number to ensure that the method that does the verification is working correctly.

Integration tests, on the other hand, are used to verify the correct functioning of our application’s integrations with external processes or applications. For example, imagine that your application will fetch a user’s data from an external API. To ensure that this works, it is possible to create an integration test that simulates the call to the API and creates success or error scenarios.

It is important to point out that many companies have as a basic requirement the coverage of at least 80% of the code with unit and integration tests.

To create situations that will simulate the execution of program functions, we normally use mocks, which in programming mean data and false functions used to facilitate the implementation of unit tests.

Telerik JustMock is a fast, flexible and complete tool for creating unit tests.

👉 You can check out this article—Using Mocks While Unit Testing ASP.NET Core Apps—that teaches you how to implement unit tests using JustMock.

Consider Using Distributed Caching

A distributed cache is a cache that can be shared by multiple servers, is typically maintained as an external service, and is available to all applications on the server.

Using distributed caching can improve the performance and scalability of an ASP.NET Core app as it sends consistent data between requests. It is also able to survive server restarts and app deployments and it doesn’t use local memory.

There are great tools that help implement distributed caching, perhaps the best known of which is Redis. Redis is an open-source (BSD licensed) in-memory data structure store that, in addition to the NoSQL database, is also used for caching and message brokering. Companies like Stackoverflow, Twitter and GitHub use Redis in their systems.

The following code snippet demonstrates how Redis can be used to cache a Minimal API built on ASP.NET Core:

app.MapGet("/orders/redis", async (IDistributedCache distributedCache) =>
{
	using (var session = DocumentStoreHolder.Store.OpenSession())
	{
		var orders = session.Query<Order>().ToList();
		
		var cacheKey = "orderList";
		
		string serializedOrders;
		
		var orderList = new List<Order>();
		
		var redisOrders = await distributedCache.GetAsync(cacheKey);
		
		if (redisOrders != null)
		{
			serializedOrders = Encoding.UTF8.GetString(redisOrders);
			orderList = JsonConvert.DeserializeObject<List<Order>>(serializedOrders);
}
else
{
	orderList = orders.ToList();

	serializedOrders = JsonConvert.SerializeObject(orderList);

		redisOrders = Encoding.UTF8.GetBytes(serializedOrders);

		var options = new DistributedCacheEntryOptions()
			.SetAbsoluteExpiration(DateTime.Now.AddMinutes(10))
			.SetSlidingExpiration(TimeSpan.FromMinutes(2));

		distributedCache?.SetAsync(cacheKey, redisOrders, options);
	}
	return orderList;
   }
});

👉 You can check out this article that teaches how to implement distributed caching with Redis from scratch: Improving Performance With Distributed Caching.

Use Tag Helpers Whenever Possible

Tag Helpers is an alternative syntax for HTML Helpers, but it provides some functionality that was difficult or impossible to achieve with helper methods.

The Tag Helpers syntax looks a lot like HTML Helpers but is rendered by Razor on the server.

The biggest advantage of using Tag Helpers is the reduction in the amount of code we have to write when creating a new application and also the fact that they create an abstract layer between the UI and the server-side code. Another important point is that Tag Helpers do not replace HTML tags, so we can use them together, depending on where they fit better.

👉 Below is an example of using Tag Helpers. You can test and see the result in the REPL.

Code sample using Tag Helpers with Telerik UI for ASP.NET Core:


@addTagHelper *, Kendo.Mvc

<kendo-datasource name="dataSource1" type="DataSourceTagHelperType.Ajax" server-operation="false" page-size="10">
	<schema>
		<model id="ProductID">
			<fields>
				<field name="ProductName" type="string"></field>
				<field name="UnitPrice" type="number"></field
				<field name="UnitsInStock" type="number"></field
				<field name="Discontinued" type="bool"></field
			</fields>
		</model>
	</schema>
	<transport>
		<read url="@Url.Action("TagHelper_Products_Read", "DataSource")" />
	</transport>
</kendo-datasource>

<kendo-grid name="grid" datasource-id="dataSource1" auto-bind="false" height="300">
	<pageable enabled="true">
	</pageable>
	<columns>
		<column field="ProductName"></column>
		<column field="UnitPrice" format="{0:c}"></column>
		<column field="UnitsInStock"></column>
		<column field="Discontinued"></column>
	</columns>
	<scrollable enabled="true" />
</kendo-grid>
<br/>

<script>
$(function (){
	dataSource1.read();
});
</script>

Optimize Data Access

Accessing the database is a common routine in a web application and can be executed thousands of times depending on the situation, so you should always think about how to optimize queries to the database to avoid possible bottlenecks.

Below is a list of data access best practices that you can use when creating a new ASP.NET Core application.

  • Create simple logic: Always try to keep the code as simple as possible. There are alternatives such as the use of functions, ternary operators, LINQ and others that help to create a clean and performative code.

  • Use asynchronous methods for API calls: Using asynchronicity allows several actions to be executed at the same time, as they do not depend on each other, which results in a great performance gain.

  • Use aggregated LINQ queries to filter records: LINQ has several useful methods to filter database queries, such as .Where(), .Sum() and .Select() extension methods.

  • Get only the necessary data: Whenever the system needs to search the database, consider returning only the really necessary data. For that, add filters to the query.

  • Consider using non-tracking queries: For data reading, consider using non-tracking queries through the Entity Framework. This frees up the workload used to monitor changes to database entity values. Just use the .AsNoTracking() extension method.

Conclusion

As we know, it is practically impossible to create a system that works perfectly in every way—but if you follow the best practices described throughout this article, your application will certainly be protected against many bugs and failures that may arise along the way. Therefore, whenever possible, try to use them in your projects.


Find more ASP.NET Core Basics posts here.

assis-zang-bio
About the Author

Assis Zang

Assis Zang is a software developer from Brazil, developing in the .NET platform since 2017. In his free time, he enjoys playing video games and reading good books. You can follow him at: LinkedIn and Github.

Related Posts

Comments

Comments are disabled in preview mode.