Dependency injection support with .NET Core

Microsoft has now supplied support for dependency injection directly with .NET Core in the form of extensions with the Microsoft.Extensions.DependencyInjection NuGet package. The support for DI in .NET Core is in a way that you can either use the provided DI implementation, or you can use given DI interfaces in Microsoft.Extensions.DependencyInjection.Abstractions and use it with the DI framework implementation of your choice, for example, Ninject.

Just to give you an idea of the .NET Core direction, a similar behavior or style is also designed with a logging framework in .NET Core using the Microsoft.Extensions.Logging.Abstractions package.

Let's look at an example to see the Microsoft DI in motion with .NET Core.

The sample code needs to call an airport interface in order to get the list of arrival flights, and it also needs to do the logging in between; so both of these dependencies need to be injected. Consider the following code:

    public class ScheduleWorker 
{
private IServiceProvider _provider;
private ILogger _logger;

public ScheduleWorker(IServiceProvider provider,
ILogger logger)
{
_provider = provider;
_logger = logger;
}

public void ExecuteSchedules()
{
_logger.LogInformation("Executing schedules at {UTCTime}",
DateTime.UtcNow);

IAirportFlightSchedules airportFlightSchedules =
_provider.GetRequiredService<IAirportFlightSchedules>();

_logger.LogInformation("Getting schedules..");
var arrivalSchedules = airportFlightSchedules.
GetDailyArrivalSchedules(DateTime.UtcNow.Date);

_logger.LogInformation("{FlightCount} schedules found",
arrivalSchedules.Count);
}
}

From the preceding code, you can see that the dependencies are injected via the constructor. Moreover, another business interface object is retrieved via IServiceProvider DI supplied interface, which shows the way that's similar to service locator pattern. Consider the following code:

    public class AirportFlightSchedules : IAirportFlightSchedules 
{
private ILogger _logger;

public AirportFlightSchedules(ILogger logger)
{
_logger = logger;
}

public IList<string> GetDailyArrivalSchedules(DateTime date)
{

From this preceding code, you can see that even AirportFlightSchedules needs the logger dependency to be injected.

Let's take a look at how the DI container was configured in order to insert interfaces:

    public class DICTests 
{
private static IServiceProvider Provider { get; set; }

[Fact]
public void Test_Simple_DIC()
{
RegisterServices();
ExecuteScedule();
}

private void RegisterServices(bool bUseFactory = false)
{
IServiceCollection services = new ServiceCollection();

//Adding required dependencies to the DI Container
//Note: DebugLogger only available when Debugger
is attached
services.AddTransient<ILogger, DebugLogger>(provider =>
new DebugLogger(typeof(DICTests).FullName));
services.AddTransient<IAirportFlightSchedules,
AirportFlightSchedules>();
services.AddSingleton(typeof(ScheduleWorker));

if(bUseFactory) ConfigureServices(services);

Provider = services.BuildServiceProvider();
}

Notice that the DI container even allows you the capability to turn a normal class into a singleton very easily so that the same instance/object is given back to the client/consumer who needs its interface. Please ignore bUseFactory we see it next as it is used to control the injection of an abstract factory class.

Our client code looks so simple and neat:

    private void ExecuteScedule() 
{
var scheduleWorker =
Provider.GetRequiredService<ScheduleWorker>();
scheduleWorker.ExecuteSchedules();
}

Upon this call, all the dependencies are automatically injected into the scheduleWorker object as well as all the dependent object's hierarchy in the usage.

Now let's come to the Abstract Factory injection part, why would we need it? There could be some situations where we would need to create objects via the relevant abstract factory object and also some logic may need to be performed when creating new object(s) via such a factory in each call to the factory. In such situations, instead of binding the abstract factory class directly with the client code, we can better use the DI to inject our desired abstract factory class and the client retrieves the factory interface from the DI to create the objects it needs. This does not only decouple the factory object from the client but also gives the capability to inject the different abstract factory for a different configuration; a fake factory for instance.

I will provide you with another example, adding to this DI sample code in order to show the factory method pattern in its own section more clearly. Here, you will see how the factory class is inserted and configured as a singleton and used in a client code to create the specific object for the given interface. Let's take a look at the following code:

    private void ConfigureServices(IServiceCollection 
serviceCollection)
{
serviceCollection.AddSingleton<IAirportFlightSchedulesFactory,
AirportFlightSchedulesFactory>();
}

[Fact]
public void Test_Simple_DIC_WithFactory()
{
RegisterServices(true);
ExecuteSceduleWithFactory();
}

private void ExecuteSceduleWithFactory()
{
var scheduleWorker = Provider.
GetRequiredService<ScheduleWorker>();
scheduleWorker.ExecuteSchedulesUsingFactoryViaDI();
}

Look at the client code using the factory to create the required objects based on the IAirportFlightSchedules interface:

    public void ExecuteSchedulesUsingFactoryViaDI() 
{
_logger.LogInformation("Executing schedules at {UTCTime}",
DateTime.UtcNow);

var factory = _provider.GetRequiredService
<IAirportFlightSchedulesFactory>();
IAirportFlightSchedules airportFlightSchedules =
factory.CreateAirportFlightSchedules();

_logger.LogInformation("Getting schedules..");
var arrivalSchedules =
airportFlightSchedules.GetDailyArrivalSchedules(
DateTime.UtcNow.Date);

_logger.LogInformation("{FlightCount} schedules found",
arrivalSchedules.Count);

That's enough about dependency injection, and I hope you learned and enjoyed it just like I did. Let's jump onto lots of other design patterns.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.131.10