In this days inversion of control in everywhere. That is a fact. We also face this pattern when creating ASP.NET Core applications. So let’s ask our self the question if our current solution in using this design pattern. There can only be one answer “No”. And that is a shame. Because the answer should be “Yes”. So let’s try to change this situation.
First let’s wind a place when the inversion of control approach can be useful. The answer is simple. Inversion of control pattern should to use while getting hello message. So let’s to that. We need to start by creating an interface and a class. That will be used to get hello message.
Interface
namespace CoreDemo { public interface IHelloService { string GetHelloMessage(); } }
Class
namespace CoreDemo { public class HelloService: IHelloService { public string GetHelloMessage() { return "Example Hello"; } } }
As we can see, IHelloService and HelloService ale very simple. Get just return string. We will change it soon, don’t worry. At this moment we need to concentrate at the Inversion of control. So let’s try to use this new service according to this pattern.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace CoreDemo { public class Startup { public Startup(IHostingEnvironment hostingEnvironment) { var configurationBuilder = new ConfigurationBuilder() .SetBasePath(hostingEnvironment.ContentRootPath) .AddJsonFile("appsettings.json"); this.Configuration = configurationBuilder.Build(); } public IConfiguration Configuration { get; set; } // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IHelloService helloService) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { await context.Response.WriteAsync(helloService.GetHelloMessage()); }); } } }
That look fine. Does not it? Configure method parameter got extended for a needed service. We used it just like, for example LoggerFactory. So let’s see if it all works.
I doesn’t. Get question is why. It looks all ok. We created an interface and a class, and added it just like other parameters it Configure method. It all should be right. Right? Wrong!!!!
Elements like LoggerFactory get registered by default. In case of custom services we unfortunately need to register them by our self. So let’s do that.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace CoreDemo { public class Startup { public Startup(IHostingEnvironment hostingEnvironment) { var configurationBuilder = new ConfigurationBuilder() .SetBasePath(hostingEnvironment.ContentRootPath) .AddJsonFile("appsettings.json"); this.Configuration = configurationBuilder.Build(); } public IConfiguration Configuration { get; set; } // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IHelloService, HelloService>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IHelloService helloService) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { await context.Response.WriteAsync(helloService.GetHelloMessage()); }); } } }
It is not hard. All we need to do is to use service method. In this case it is method to add singleton.
Final touch
Now that all the elements are in place there is only one more thing to do. We need to make HelloService actually working. So it needs to get data from the settings file and use IOC pattern.
using Microsoft.Extensions.Configuration; namespace CoreDemo { public class HelloService: IHelloService { private string _hello; public HelloService(IConfiguration configuration) { _hello = configuration["HelloMessage"]; } public string GetHelloMessage() { return _hello; } } }
This is how service looks now. The interesting thing is that it uses IConfiguration via the IOC pattern. So it needs to be registered, before it can be used. Just like in previous case. Let’s see how it is done now.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace CoreDemo { public class Startup { public Startup(IHostingEnvironment hostingEnvironment) { var configurationBuilder = new ConfigurationBuilder() .SetBasePath(hostingEnvironment.ContentRootPath) .AddJsonFile("appsettings.json"); this.Configuration = configurationBuilder.Build(); } public IConfiguration Configuration { get; set; } // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddSingleton(Configuration); services.AddSingleton<IHelloService, HelloService>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IHelloService helloService) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { await context.Response.WriteAsync(helloService.GetHelloMessage()); }); } } }
It is differed approach. In this case we add configuration object as via the singleton method. We can do that when we want to prepare object before passing it to IOC container. In can come very handy in some situations.
Karol Rogowski