2017-01-18

Introduction

In the previous post we started looking at the anatomy of an ASP.NET Core application. .NET Core is a cross-platform, highly modular and lightweight application framework that is meant to be the future of application development in .NET. We identified some significant changes compared to traditional ASP.NET applications that we saw before. There are a couple of new JSON files such as global.json and project.json. Global.asax.cs is gone and has been totally replaced by Startup.cs. There’s still a web.config file but it’s not used for the same kind of configuration as before. A different json file which we haven’t seen yet will be used for that purpose. The file structure is also different in that the source files are stored in a folder called “src” by default. Note that .NET Core is still in preview mode at the time of writing this series.

In this post we’ll look at adding dependencies and using them via dependency injection and service registration.

We started working on a demo application called DotNetCoreBookstore using the Empty template in Visual Studio 2015. We’ll continue exploring the various .NET Core features through the demo web application.

Adding dependencies to the project

You’re probably familiar with the NuGet package manager. NuGet is still the standard way of downloading libraries to our project from a central repository. So if you right-click the References node in the Solution Explorer then you’ll see the Manage NuGet Packages option like before. The Installed section shows all the installed libraries. This list is also reflected in the “dependencies” and “frameworks” section of the project.json file.

The Browse section allows us to search for a specific library by name. If we would like to install the widely popular JSON parser by Newtonsoft then it’s easy via NuGet:



We can achieve the same using the Package Manager Console – View, Other Windows, Package Manager Console. Here’s the simplest command to do just that:

“Install-Package Newtonsoft.Json”

You’ll see that JSON.NET has been added to project.json:

“Newtonsoft.Json”: “9.0.1”

…where 9.0.1 is the currently most updated stable version of JSON.NET. The library has been added to the References list with all the dependencies that JSON.NET in turn requires:



We can also manually extend the dependencies section of project.json if we know the name of package we’re looking for. Visual Studio provides InstelliSense along the way. If you start typing ‘ “New” ‘ in this section then a filtered list will appear in the screen and you can select the library you need. VS also helps with listing the available version numbers. As soon as you save the changes in project.json Visual Studio will download the required packages.

There’s still yet another way of adding libraries. If you know that a certain object or function is available in a NuGet package then you can type it in the Visual Studio editor and the Quick Actions drop down will offer the option of downloading the package from NuGet. E.g. I know that the following code is valid in JSON.NET:

The compiler won’t obviously understand this and a small light-bulb icon will appear on the left-hand side of the screen. This icon offers a number of options to remedy the situation. One of the options will be to download JSON.NET:



This option will also take care of adding the necessary “using” statement in the cs file.

Upgrading the packages

While we’re at it it’s time to upgrade the .NET Core version to the most recent version 1.1.0 from 1.0.1 where we originally started. By the time you read this post there might be a newer version. Check out the updates section in NuGet and you may see a number of available updates including one for Microsoft.NETCore.App:

It’s OK to update the other packages as well. If you build the project after the update you might get a build error:

“Can not find runtime target for framework ‘.NETCoreApp,Version=v1.0’ compatible with one of the target runtimes: ‘win10-x64, win81-x64, win8-x64, win7-x64’.”

This is apparently quite common when people upgrade the .NET Core library in this way according to the StackOverflow thread here.

The suggested solution works so we add the following section to project.json:

"runtimes": {

"win10-x64": {}

}

The necessary NuGet packages will be restored and the application should run as before. We can expect new releases of .NET Core quite often so it’s good to check the Updates section in the NuGet manager relatively often.

Dependency injection and dependency inversion

If you’ve never come across the term “dependency injection” (DI) and the dependency inversion principle (DOP) then you may think they are some scary features involving lots of complex syntax. Truth is that even the most junior developers use some kind of dependency injection in their code without knowing the exact name for the technique. There are numerous posts on this blog dedicated to DI like here, here or here.

The Dependency Inversion Principle (DIP) helps to decouple your code by ensuring that you depend on abstractions rather than concrete implementations where abstractions can be either interfaces or abstract base classes. Dependency Injection (DI) is an implementation of this principle. In fact DI and DIP are often used to mean the same thing. A key feature of DIP is programming to abstractions so that consuming classes can depend on those abstractions rather than low-level implementations. DI is the act of supplying all classes that a service needs rather than letting the service obtain its concrete dependencies.

Startup.cs shows examples of dependency injection where a number of abstractions describe the types used in the ConfigureServices and Configure methods:

These are examples of method injection, i.e. where a method describes the types of services that it needs in its signature. These objects are required by the function to fulfil its tasks. The caller will be able to provide concrete implementations of those abstractions but the functions won’t care whatsoever.

You may well have used various tools that automate the registration and resolution of dependencies, so-called IoC (Inversion of Control) containers such as StructureMap. .NET Core now includes a built-in IoC which might replace external IoC containers. I wrote “might” since at this point I’m not sure whether the .NET Core IoC container has all the features supported by the various IoC tools available out there. If you happen to have more information on this then feel free to comment.

Let’s first look at the Configure method which requires 3 objects by default. The first one IApplicationBuilder will look familiar to those who have worked with OWIN and Katana in full-blown ASP.NET. It is the .NET Core equivalent of the IAppBuilder interface we looked at in this post. It helps us build the request pipeline of the web application. We can also add OWIN middleware to the pipeline. We’ll explore middleware in a later module, don’t worry about it yet.

IHostingEnvironment opens a gateway to a number of properties of the running application such as the application name, its root path and its environment name. These will also play an important role later on.

ILoggerFactory allows us to configure logging for the application. We won’t spend any time on this yet but if you’d like to find out more right now then this page is helpful.

The big question is what kind of concrete types these abstractions get when running the web application. After all the caller cannot provide a new IApplicationBuilder like here:

That’s obviously invalid code that won’t compile. If you set a breakpoint within the Configure method and run the application then you’ll be able to see the concrete types by hovering over the parameter names:

Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder

Microsoft.AspNetCore.Hosting.Internal.HostingEnvironment

Microsoft.Extensions.Logging.LoggerFactory

Where have these concrete types been registered? It turns out that ASP.NET Core pre-configured them for us as default types. We need to investigate the ConfigureServices method where the incoming IServiceCollection holds the list of configured services.

Insert the following code within ConfigureServices:

…where Debug is located in the System.Diagnostics namespace. Re-run the application. The services collection includes 19 preconfigured services at this time. This number can certainly vary as .NET Core matures. We can think of a service as key-pairs of abstractions and concrete types. Here are some examples:

Microsoft.AspNetCore.Http.IHttpContextFactory: Microsoft.AspNetCore.Http.HttpContextFactory, Transient

Microsoft.Extensions.ObjectPool.ObjectPoolProvider: Microsoft.Extensions.ObjectPool.DefaultObjectPoolProvider, Singleton

Microsoft.Extensions.Options.IOptionsSnapshot`1[TOptions]: Microsoft.Extensions.Options.OptionsSnapshot`1[TOptions], Scoped

I selected these three so that we see all three lifetime types:

Transient: every time an abstract type is required a new concrete type will be created. Every object that requires IHttpContextFactory will get its own instance of HttpContextFactory which is the default concrete type of this abstraction

Singleton: the exact opposite of Transient. Every object will get the same instance of the concrete type

Scoped: a new concrete type is instantiated for each HTTP request, i.e. the concrete type is a singleton only within the same request. If the same type of abstraction is required multiple times within the same HTTP request then the same instance will be served to the dependent objects

We can fortunately use the IServiceCollection object to register our own dependencies. Add the following interface and implementation to the solution to a new folder called Dependencies:

We have no controllers yet so we’ll misuse Startup.cs for the demo. Extend the signature of Configure to include the IStringFormatter interface:

Start the application now and you should get an error screen with messages similar to the following:

InvalidOperationException: No service for type ‘DotNetCoreBookstore.Dependencies.IStringFormatter’ has been registered.

Exception: Could not resolve a service of type ‘DotNetCoreBookstore.Dependencies.IStringFormatter’ for the parameter ‘stringFormatter’ of method ‘Configure’ on type ‘DotNetCoreBookstore.Startup’.

We can wire up the dependencies in the ConfigureServices method. The incoming IServiceCollection object has various Add methods such as Add, AddScoped, AddSingleton and AddTransient. This is where we must consider the lifetime of the dependency: scoped, transient or singleton. For the demo it doesn’t really make any different so we’ll just add a singleton. It can be written as follows using the generic version of AddSingleton:

We can now use the stringFormatter parameter in the Configure method:

We’ll now see our message in the browser window as follows:

{“Message”:”Hello World!”}

What if JsonStringFormatter also requires a dependency? Add the following interface and implementation to the Dependencies folder:

Update JsonStringFormatter so that it requires an IGreeter through its constructor:

If we run the application now we get the same kind of exception as above:

InvalidOperationException: Unable to resolve service for type ‘DotNetCoreBookstore.Dependencies.IGreeter’ while attempting to activate ‘DotNetCoreBookstore.Dependencies.JsonStringFormatter’.

The solution is the same. We have to register the abstract dependency along with a concrete type. We’ll add the good morning greeter with a transient lifetime:

The objects are now correctly instantiated so we get the following message in the web browser:

{“Greeting”:”Good morning”,”Content”:{“Message”:”Hello World!”}}

It may be so that we don’t know exactly which implementation of an abstraction we want to have constructed by ASP.NET. There might be various IGreeter implementations like GoodEveningGreeter and GoodNightGreeter. Here the exact implementation may depend on the time of day. One way to solve that problem is to use the overloaded Add method which accepts a Func which returns an IGreeter. It accepts an IServiceProvider object with which we can retrieve the registered dependencies. We’ll ignore it for the time being but the “provider” parameter in the following minimal implementation is of type IServiceProvider:

The application will continue to function as before.

That’s enough for now, we’ll continue in the next post.

View the list of MVC and Web API related posts here.

Show more