Boosting Code Readability and Manageability in ASP.NET Core
In Software development, code readability transforms complexity into clarity. Martin Fowler said:
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
Code should be readable and manageable so other developers can understand and work without additional fatigue. .NET has many ways to enhance readability and manageability. This post will explore C#'s extension methods by listing five use cases that I use on all of the ASP.NET Core projects I'm working on.
What are Extension methods in C#?
In C#, the extension method allows developers to add new functionalities to existing types and classes without accessing or modifying the code. They were introduced in C# 3.0 and have been a core part of the .NET ecosystem for better programming offering a clean and flexible approach to code organization.
Let's see some use cases for adding extension methods. You don't need any special permission or a deep understanding of the class. Just define a static class with a static method and write your customized logic in it. The first argument of the method should be the original class whose method you are adding with this keyword.
Use case 1: ASP.NET's IServiceCollection
extension method for dependency injection
We can add an extension method to ASP.NET Core applications IServiceCollection
for code readability. This collection registers dependency injections used in web applications such as repositories, services, mappers, custom middleware, etc. This reduces the code complexity in Program.cs
by separating register logic with extension methods. The technique is handy for scalable applications with hundreds of injecting services crowding Program.cs
files.
public static class DependencyInjection
{
public static void InjectedServices(this IServiceCollection services)
{
// User DI
services.AddScoped<IUserRepo, UserRepo>();
services.AddScoped<IUserService, UserService>();
services.AddScoped<ILanguageRepo, LanguageRepo>();
services.AddScoped<ILanguageService, LanguageService>();
// Material Repos
services.AddScoped<IMaterialRepo, MaterialRepo>();
services.AddScoped<IMaterialService, MaterialService>();
services.AddScoped<ICostRepo, CostRepo>();
services.AddScoped<ICostService, CostService>();
services.AddScoped<ITravelCostRepo, TravelCostRepo>();
services.AddScoped<ITravelCostService, TravelCostService>();
services.AddScoped<NotifyMiddleware>();
services.AddHttpContextAccessor();
}
}
You can see I have added all the registering logic in the extension method of IServiceCollection
. I further clustered dependency injections into regions for better manageability. We have reduced the Program
class fatigue with the extension method.
Use case 2: String extension method for extending string functionality
Extension methods can add some user-defined functionality to common classes like strings:
public static class StringExtensions
{
public static bool IsValidEmail(this string email)
{
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
}
// Usage
string email = "example@domain.com";
bool isValid = email.IsValidEmail();
Console.WriteLine(isValid); // Output: True
email = "exampleomain.com";
isValid = email.IsValidEmail();
Console.WriteLine(isValid); // Output: False
Use case 3: Simplifying user authorization with ClaimsPrincipal
Extension methods provide a clean way to check the role from ClaimsPrincipal
of the System.Security.Claims
namespace:
public static class ClaimsPrincipalExtensions
{
public static bool IsOneOf(this ClaimsPrincipal user, params string[] roles)
{
return user?.Identity?.IsAuthenticated == true && roles.Any(role => user.IsInRole(role));
}
}
// Usage
if (User.IsOneOf("Admin", "Superadmin"))
{
// Admin-specific logic
}
Use case 4: Data validation in MVC controllers
Similarly, we can apply controller validation in a more readable way with ModelStateDictionary
extension method:
public static class ModelStateExtensions
{
public static void AgeValidationError(this ModelStateDictionary modelState, string key, string message)
{
modelState.AddModelError(key, message);
}
}
// Usage in a controller
if (model.Age < 18)
{
ModelState.AddValidationError("Age", "Age must be 18 or older.");
}
Use case 5: Filtration method in DbContext
We can extend DbContext
to enclose methods for custom filtration. This provides a shorthand to get filtered results and avoid writing complex LINQ queries multiple times:
public static class DbContextExtensions
{
public static IQueryable<User> GetYoungUsers(this DbContext context)
{
return context
.Set<User>()
.Where(u => u.Age >= 18 && u.Age <= 40)
.ToList();
}
}
// Usage
var youngUsers = _context.GetYoungUsers();
Benefits of extension methods
- Improve Readability: Extension methods separate complex logic by encapsulating it in a separate extension class making the main code easy to read and understand
- Enhance Maintainability: Logics are enclosed in a central place that makes it easy to change the code when needed.
- Reusability: While the code is at the central extension method, Users don't need to write that code again and again. Rather the extended method can be called where required.
- Enhance code organization: Implementation of the extension method fulfills one of the most important concepts of code organization - separation of concern. Separate classes hold the definition of separate logic.
- Better Domain-Specific Language (DSL) Creation: They enable more domain-specific implementation for the business logic and domain.
Summary
Developers create applications, and they need ease of readability and understanding. The more readable an application is, the better and easier its maintainability is. For clean code, the .NET ecosystem utilizes C# extension methods that make its classes and structs extendable. That means users can add custom methods in any class, centralizing their logic and using those methods as the other methods of that class.
We have seen a few examples of how the extension method reduces code complexity and separates logic. Besides, it adds functionality to any built-in or user-defined class without changing its code. With the power of extension methods, developers can write readable and manageable code.
elmah.io: Error logging and Uptime Monitoring for your web apps
This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.
See how we can help you monitor your website for crashes Monitor your website