If you're an ASP.NET Core guy or gal, you should be familiar with the nightmare of startup file organization. Every ASP.NET Core application startup file can include configurations, middleware declaration, dependency injection definition, authentication configs, and policies, among other things. So, long story short- heavy applications have large messy unreadable startup files with hundreds of lines of the "hard to scroll through" code.
So, as you might have guessed, here is where extension methods come to the rescue. Learning to organize your Startup.cs is part of being a good C# programmer and having extension methods in your arsenal is a must. By the end of this article, you will learn a neat and simple trick to improve the organization of your startup files and spare yourself the pain of endless startup file scrolling in your future projects.
Meet extension methods, your new best friends!
"Extension Method" was introduced with C# 3.0, and since then, the sun has been shining a little brighter. Extension methods can be used to extend an existing type without creating a derived type, recompiling, or modifying the original one.
As a brief example, let's say we want to add a special method CountChar to the class String using an extension method. CountChar will simply count the number of occurences of a specific character in a given string.
- namespace ExtensionMethods {
- public static class MyExtensions {
- public static int CountChar(this String str, Char character) {
- return str.Count(x => x == character);
- }
- }
- }
Now, to use it, we just need to import the name space ExtensionMethods in our project, and voilà! Thanks to the magic of IntelliSense, our new method CountChar is now usable for any object of type String.
Unleash the full potential of extension methods
Now, let's do something more complex. We will extend the IServiceCollection and the IApplicationBuilder.
To do that, we need to,
- Create a static class
This class will include all our extension methods
- Create our extension method passing our type as a parameter with the keyword this.
In our example, we will take Swagger configuration as an example.
- namespace ExtensionMethods {
- public static class SwaggerConfigurationExtension {
- public static void AddSwaggerConfig(this IServiceCollection services) {
- services.AddSwaggerGen(c => {
- c.SwaggerDoc("v1", new OpenApiInfo {
- Title = "My API", Version = "v1"
- });
- });
- }
- public static void UseCustomSwaggerConfig(this IApplicationBuilder app) {
-
- app.UseSwagger();
-
-
- app.UseSwaggerUI(c => {
- c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
- });
- }
- }
- }
In the code above, we created two extension methods.
- AddSwaggerConfig extends the IServiceCollection to add the swagger generator configuration.
- UseCustomSwaggerConfig extends the IApplicationBuilder to enable middleware for swagger server generation and swagger UI.
The calls in the startup file will be done as below.
- public class Startup {
- public void ConfigureServices(IServiceCollection services) {
- services.AddSwaggerConfig()
- }
- public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
- app.UseCustomSwaggerConfig()
- }
- }
In terms of startup extension organization, two possibilities present themselves.
- Regoup all your extensions in a single class (called StartupExtension for example).
- Create a class for each feature or service added to the IServiceCollection.
Personally, I prefer the second solution. Usually, I create a folder named Extensions and I put all my classes there. With this structure, I can re-use an extension in the other project with copying just the file that I need rather than copying the whole code.
So, with those methods, we will have a startup file that looks like this.
- public class Startup {
- public void ConfigureServices(IServiceCollection services) {
- services.AddSwaggerConfig()
- services.AddCustomAuthentification()
- services.AddCustomSettingsFile()
- services.AddHttpClients()...
- }
- public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
- app.UseCustomSwaggerConfig()...
- }
- }
Congratulations, now you've harnessed the power of extension methods.
I hope you enjoyed this article.