ASP.NET Core Dependency
Injection Lifetime
▌Introduction
We can inject built-in framework services or our services
in ASP.NET Core by configuring the container's services in Startup.cs: ConfigureServices
method.
See more information on Dependency Injection in As
We will learn the lifetime of the four injection ways,
1.
Transient
New instance is provided to every controller and every service.
New instance is provided to every controller and every service.
2.
Scoped
Service are created once per request.
Service are created once per request.
3.
Singleton
Single instance throughout the application, lazy singleton.
Single instance throughout the application, lazy singleton.
4.
Singleton Instance
Create instance when registered, eager singleton.
Create instance when registered, eager singleton.
▌Environment
▋.NET Core 2.0.0 preview 1
▌Sample
▋Create custom services
Here we will create several interfaces to identify Transient, Scoped, Singleton, Singleton-instance services.
▋Service interface
public interface IGuidService
{
string Title { get; set; }
Guid Id { get; set; }
}
public interface IGuidServiceTransient : IGuidService
{}
public interface IGuidServiceScoped : IGuidService
{}
public interface IGuidServiceSingleton : IGuidService
{}
public interface IGuidServiceSingletonInstance : IGuidService
{}
|
▋Service class
public class GuidService :
IGuidService, IGuidServiceTransient, IGuidServiceScoped,
IGuidServiceSingleton, IGuidServiceSingletonInstance
{
public GuidService(string title, Guid?
guid = null)
{
this.Title = title;
if (guid != null &&
!guid.Equals(Guid.Empty))
this.Id =
(Guid)guid;
else
this.Id =
Guid.NewGuid();
//Leave a log
when the service instance is created
LogUtility.Logger.Warn($"{title} : completed
constructor.");
}
public string Title { get; set; }
public Guid Id { get; set; }
}
|
▋Register services
▋Startup.cs
public void
ConfigureServices(IServiceCollection services)
{
//Transient
services.AddTransient<IGuidServiceTransient>(provider => new GuidService(title: "Transient"));
//Scoped
services.AddScoped<IGuidServiceScoped>(provider => new GuidService(title: "Scoped"));
//Singleton
services.AddSingleton<IGuidServiceSingleton>(provider => new GuidService(title: "Singleton"));
//Singleton instance
services.AddSingleton<IGuidServiceSingletonInstance>(new GuidService("Instance", Guid.Empty));
}
|
▋Inject services to see the lifetime
We will log the GUID when receiving a request.
▋Controller
public class DIdemoController : Controller
{
private IGuidServiceTransient _guidServiceTransient = null;
private IGuidServiceScoped _guidServiceScoped = null;
private IGuidServiceSingleton _guidServiceSingleton = null;
private IGuidServiceSingletonInstance _guidServiceSingletonInstance = null;
public
DIdemoController(
IGuidServiceTransient guidServiceTransient,
IGuidServiceScoped guidServiceScoped,
IGuidServiceSingleton guidServiceSingleton,
IGuidServiceSingletonInstance guidServiceSingletonInstance
)
{
this._guidServiceTransient
= guidServiceTransient;
this._guidServiceScoped
= guidServiceScoped;
this._guidServiceSingleton
= guidServiceSingleton;
this._guidServiceSingletonInstance
= guidServiceSingletonInstance;
}
public IActionResult Index()
{
_logger.Debug($"Action:
Index");
_logger.Info($"Transient : {this._guidServiceTransient.Id}");
_logger.Info($"Scoped : {this._guidServiceScoped.Id}");
_logger.Info($"Singleton : {this._guidServiceSingleton.Id}");
_logger.Info($"Instance : {this._guidServiceSingletonInstance.Id}");
return View();
}
}
|
▋Result on first and second request
The result shows that the Singleton
service was created only ONCE on demand (receiving a request), thus the
two requests get the same GUID.
However, Transient and Scoped services were created every time we send a request.
However, Transient and Scoped services were created every time we send a request.
But what the difference between Transient and Scoped
ones? We will talk about it later on the next demo.
Notice that we didn’t have any construction log with the
Singleton-Instance service, why? The reason is that the
Singleton-Instance
service was created when we registered it on this line:
services.AddSingleton<IGuidServiceSingletonInstance>(new GuidService("Instance", Guid.Empty));
And thaz why I called it an eager singleton.
However my log utility was initialized after the above
code, so we didn’t get any log when it was created :- )
▋Inject services to tell the difference between Transient and Scoped
Let’s modify the MVC controller to inject multiple
services on Transient and Scoped.
▋Controller
public class DIdemoController : Controller
{
private IGuidServiceTransient _guidServiceTransient = null;
private IGuidServiceScoped _guidServiceScoped = null;
private IGuidServiceTransient _guidServiceTransientDup = null;
private IGuidServiceScoped _guidServiceScopedDup = null;
public
DIdemoController(
IGuidServiceTransient guidServiceTransient,
IGuidServiceScoped guidServiceScoped,
IGuidServiceTransient guidServiceTransientDup,
IGuidServiceScoped guidServiceScopedDup)
{
this._guidServiceTransient
= guidServiceTransient;
this._guidServiceScoped
= guidServiceScoped;
this._guidServiceTransientDup
= guidServiceTransientDup;
this._guidServiceScopedDup
= guidServiceScopedDup;
}
public IActionResult Index()
{
_logger.Debug($"Action:
Index");
_logger.Info($"Transient : {this._guidServiceTransient.Id}");
_logger.Info($"Scoped : {this._guidServiceScoped.Id}");
_logger.Info($"Transient(2)
: {this._guidServiceTransientDup.Id}");
_logger.Info($"Scoped(2) : {this._guidServiceScopedDup.Id}");
return View();
}
}
|
▋Result
We found that Scoped service was only created once!
And Transient service was created on
every injection. So the request got the same GUID on Scoped service.
▌Summary
How we choose the right way to inject our service? Here
are some suggestions.
1.
Use Transient on stateless service.
2.
Use Scoped to ensure the same instance
in a request.
3.
Use Singleton to keep or deal with
application level information, but it should be a thread-safe service.
4.
Use Singleton-Instance to create instance on Application
startup for application level usage.
▌Reference
沒有留言:
張貼留言