Dependency Injection in ASP. NET Core - a quick overview
CountryService
which provides a list of countries. We also need an interface for this service, let's create it too:public class CountryService : ICountryService
{
public IEnumerable<Country> All()
{
return new List<Country>
{
new Country {Code = "DE", Name = "Germany" },
new Country {Code = "FR", Name = "France" },
new Country {Code = "CH", Name = "Switzerland" },
new Country {Code = "IT", Name = "Italy" },
new Country {Code = "DK", Name = "Danmark" } ,
new Country {Code = "US", Name = "United States" }
};
}
}
public interface ICountryService
{
IEnumerable<Country> All();
}
public class Country
{
public string Code { get; internal set; }
public string Name { get; internal set; }
}
Register the services
ContryService
to the DI container. This needs to be done in the Startup.cs
in the method ConfigureServices
:services.AddTransient<ICountryService, CountryService>();
IContryService
, you'll get a new instance of the CountryService
. This is what transient means in this case. You are also able to add singleton mappings (using AddSingleton
) and scoped mappings (using AddScoped
). Scoped in this case means scoped to a HTTP request, which also means it is a singleton while the current request is running. You can also add an existing instance to the DI container using the method AddInstance
.IServiceCollection
:services.AddTransient<ICountryService, CountryService>();
services.AddTransient(typeof (ICountryService), typeof (CountryService));
services.Add(new ServiceDescriptor(typeof(ICountryService), typeof(CountryService), ServiceLifetime.Transient));
services.Add(new ServiceDescriptor(typeof(ICountryService), p => new CountryService(), ServiceLifetime.Transient));
services.AddSingleton<ICountryService, CountryService>();
services.AddSingleton(typeof(ICountryService), typeof(CountryService));
services.Add(new ServiceDescriptor(typeof(ICountryService), typeof(CountryService), ServiceLifetime.Singleton));
services.Add(new ServiceDescriptor(typeof(ICountryService), p => new CountryService(), ServiceLifetime.Singleton));
services.AddScoped<ICountryService, CountryService>();
services.AddScoped(typeof(ICountryService), typeof(CountryService));
services.Add(new ServiceDescriptor(typeof(ICountryService), typeof(CountryService), ServiceLifetime.Scoped));
services.Add(new ServiceDescriptor(typeof(ICountryService), p => new CountryService(), ServiceLifetime.Scoped));
services.AddInstance<ICountryService>(new CountryService());
services.AddInstance(typeof(ICountryService), new CountryService());
services.Add(new ServiceDescriptor(typeof(ICountryService), new CountryService()));
IServiceCollection
to keep the Startup.cs
clean. The same way is used by default for MVC and many other tools you want to use in your project:services.AddMvc();
IServiceCollection
which are needed by the MVC MiddleWare.public static class ServiceCollectionExtensions
{
public static IServiceCollection RegisterServices(
this IServiceCollection services)
{
services.AddTransient<ICountryService, CountryService>();
// and a lot more Services
return services;
}
}
RegisterServices
looks now much more cleaner:public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.RegisterServices();
}
Usage
CountryService
almost everywhere in our ASP.NET Core application. For example in a MVC controller:public class HomeController : Controller
{
private readonly ICountryService _countryService;
public HomeController(ICountryService countryService)
{
_countryService = countryService;
}
// …
}
@inject DiViews.Services.ICountryService CountryService;
@inject
directive defines the interface. The second part is the name of the variable which holds our instance._ViewImports.cshtml
. In a complete new ASP.NET Core project, there is already a global injection defined for ApplicationInsights:@inject Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration TelemetryConfiguration
@if (countryService.All().Any())
{
<ul>
@foreach (var country in CountryService.All().OrderBy(x => x.Name))
{
<p>@country.Name (@country.Code)</p>
}
</ul>
}
@Html.DropDownList("Coutries", CountryService.All()
.OrderBy(x => x.Name)
.Select(x => new SelectListItem
{
Text = x.Name,
Value = x.Code
}))
public class CountryListTagHelper : TagHelper
{
private readonly ICountryService _countryService;
public CountryListTagHelper(ICountryService countryService)
{
_countryService = countryService;
}
public string SelectedValue { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "select";
output.Content.Clear();
foreach (var country in _countryService.All())
{
var seleted = "";
if (SelectedValue != null && SelectedValue.Equals(country.Code, StringComparison.CurrentCultureIgnoreCase))
{
seleted = " selected=\"selected\"";
}
var listItem = $"<option value=\"{country.Code}\"{seleted}>{country.Name}</option>";
output.Content.AppendHtml(listItem);
}
}
}
<country-list selected-value="@Model.Country"></country-list>
Conclusion
ASP.NET Core Service Lifetimes (Infographic)
ASP.NET Core supports the dependency injection (DI) software design pattern that allows us to register services and control how these services will be instantiated and injected in different components. Some services will be instantiated for a short time and will be available only in a particular component and request. Some will be instantiated just once and will be available throughout the application. Here are the service lifetimes available in ASP.NET Core.
Singleton
A single instance of the service class is created, stored in memory and reused throughout the application. We can use Singleton for services that are expensive to instantiate. We can register Singleton service using the AddSingleton method as follows:
Scoped
The service instance will be created once per request. All middlewares, MVC controllers, etc. that participate in handling of a single request will get the same instance. A good candidate for a scoped service is an Entity Framework context. We can register Scoped service using the AddScoped method as follows:
Transient
Transient lifetime services are created each time they’re requested. This lifetime works best for lightweight, stateless services. We can register Transient service using the AddTransient method as follows:
If you want to visualize the above concepts then here is an infographic for your quick reference.