Intro
When I use DI with ASP.NET Core app, I choose "Microsoft.Extensions.DependencyInjection".
It has three types of lifetime of instance.
- Transient
- Scoped
- Singleton
Dependency injection in ASP.NET Core | Microsoft Docs
Because I can't use "Microsoft.Extensions.DependencyInjection" with ASP.NET Framework, so when I use DI I choose Unity Container.
It also has "Transient", "Scoped", "Singleton".
"Transient"(every time creates new instances) and "Singleton"(creates only one instance in the application) are same as "Microsoft.Extensions.DependencyInjection".
But "Scoped" doesn't seem like "Microsoft.Extensions.DependencyInjection" one.
(creates instances per request)
Which types of lifetime should I choose to set "Scoped" of "Microsoft.Extensions.DependencyInjection"?
Environments
- .NET Framework ver.4.8
- Unity.Container ver.5.11.8
- Unity.Mvc ver.5.11.1
- NLog.Web ver.4.9.3
Sample projects
ProductsController.cs
using System.Web.Mvc;
using NetFrameworkSample.Lifetimes;
using NetFrameworkSample.Products;
using NLog;
namespace NetFrameworkSample.Controllers
{
public class ProductsController: Controller
{
private readonly Logger _logger;
private readonly IProductsService _product;
private readonly ISecondService _secondService;
public ProductsController(IProductsService product,
ISecondService secondService)
{
_logger = LogManager.GetCurrentClassLogger();
_logger.Debug("ProductsController Constructor");
_product = product;
_secondService = secondService;
}
[Route("")]
public ActionResult GetMessage()
{
return View("~/Views/ProductPage.cshtml");
}
}
}
ProductPage.cshtml
@{
ViewBag.Title = "ProductPage";
}
<h2>ProductPage</h2>
@Html.ActionLink("Sample", "Index", "Lifetime")
LifetimeController.cs
using NetFrameworkSample.Products;
using NLog;
using System.Web.Mvc;
namespace NetFrameworkSample.Lifetimes
{
public class LifetimeController : Controller
{
private readonly Logger _logger;
private readonly IProductsService _product;
private readonly ISecondService _secondService;
public LifetimeController(IProductsService product,
ISecondService secondService)
{
_logger = LogManager.GetCurrentClassLogger();
_logger.Debug("LifetimeController Constructor");
_product = product;
_secondService = secondService;
}
[Route("Lifetime")]
public string Index()
{
return View("~/Views/Lifetime/Index.cshtml");
}
}
}
Index.cshtml
@{
ViewBag.Title = "LifetimeSamplePage";
}
<h2>LifetimeSample</h2>
@Html.ActionLink("Sample", "GetMessage", "Products")
IProductsService.cs
namespace NetFrameworkSample.Products
{
public interface IProductsService
{ }
}
ProductsService.cs
using NetFrameworkSample.Lifetimes;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace NetFrameworkSample.Products
{
public class ProductsService: IProductsService
{
private readonly Logger _logger;
private readonly IThirdService _thirdService;
public ProductsService(IThirdService thirdService)
{
_logger = LogManager.GetCurrentClassLogger();
_logger.Debug("ProductsService Constructor");
_thirdService = thirdService;
}
}
}
ISecondService.cs
namespace NetFrameworkSample.Lifetimes
{
public interface ISecondService
{ }
}
SecondService.cs
using NLog;
namespace NetFrameworkSample.Lifetimes
{
public class SecondService: ISecondService
{
private readonly Logger _logger;
private readonly IThirdService _thirdService;
public SecondService(IThirdService thirdService)
{
_logger = LogManager.GetCurrentClassLogger();
_logger.Debug("SecondService Constructor");
_thirdService = thirdService;
}
}
}
IThirdService.cs
namespace NetFrameworkSample.Lifetimes
{
public interface IThirdService
{ }
}
ThirdService.cs
using NLog;
namespace NetFrameworkSample.Lifetimes
{
public class ThirdService: IThirdService
{
private readonly Logger _logger;
public ThirdService()
{
_logger = LogManager.GetCurrentClassLogger();
_logger.Debug("ThirdService Constructor");
}
}
}
UnityMvcActivator.cs
using System.Linq;
using System.Web.Mvc;
using Unity.AspNet.Mvc;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(NetFrameworkSample.UnityMvcActivator), nameof(NetFrameworkSample.UnityMvcActivator.Start))]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(NetFrameworkSample.UnityMvcActivator), nameof(NetFrameworkSample.UnityMvcActivator.Shutdown))]
namespace NetFrameworkSample
{
public static class UnityMvcActivator
{
public static void Start()
{
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(UnityConfig.Container));
DependencyResolver.SetResolver(new UnityDependencyResolver(UnityConfig.Container));
}
public static void Shutdown()
{
UnityConfig.Container.Dispose();
}
}
}
Transient, Scoped, Singleton
I try changing lifetime and accessing "ProductsController" and "LifetimeController".
After that, I check logs to know when and how many times are instances created.
Transient
UnityConfig.cs
using System;
using Unity;
using NetFrameworkSample.Products;
using NetFrameworkSample.Lifetimes;
namespace NetFrameworkSample
{
public static class UnityConfig
{
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
container.RegisterType<IProductsService, ProductsService>(TypeLifetime.Transient);
container.RegisterType<ISecondService, SecondService>(TypeLifetime.Transient);
container.RegisterType<IThirdService, ThirdService>(TypeLifetime.Transient);
return container;
});
public static IUnityContainer Container => container.Value;
public static void RegisterTypes(IUnityContainer container)
{ }
}
}
Result
--- Transient ---
--- Access "ProductsController" ---
2020-10-18 19:00:57.2576||DEBUG|NetFrameworkSample.Lifetimes.ThirdService|ThirdService Constructor |url: http://localhost/|action:
2020-10-18 19:00:57.3281||DEBUG|NetFrameworkSample.Products.ProductsService|ProductsService Constructor |url: http://localhost/|action:
2020-10-18 19:00:57.3281||DEBUG|NetFrameworkSample.Lifetimes.ThirdService|ThirdService Constructor |url: http://localhost/|action:
2020-10-18 19:00:57.3281||DEBUG|NetFrameworkSample.Lifetimes.SecondService|SecondService Constructor |url: http://localhost/|action:
2020-10-18 19:00:57.3281||DEBUG|NetFrameworkSample.Controllers.ProductsController|ProductsController Constructor |url: http://localhost/|action:
--- Access "LifetimeController" ---
2020-10-18 19:03:57.6918||DEBUG|NetFrameworkSample.Lifetimes.ThirdService|ThirdService Constructor |url: http://localhost/Lifetime|action:
2020-10-18 19:03:57.6918||DEBUG|NetFrameworkSample.Products.ProductsService|ProductsService Constructor |url: http://localhost/Lifetime|action:
2020-10-18 19:03:57.6918||DEBUG|NetFrameworkSample.Lifetimes.ThirdService|ThirdService Constructor |url: http://localhost/Lifetime|action:
2020-10-18 19:03:57.6918||DEBUG|NetFrameworkSample.Lifetimes.SecondService|SecondService Constructor |url: http://localhost/Lifetime|action:
2020-10-18 19:03:57.6918||DEBUG|NetFrameworkSample.Lifetimes.LifetimeController|LifetimeController Constructor |url: http://localhost/Lifetime|action:
All instances were created each time.
Singleton
UnityConfig.cs
using System;
using Unity;
using NetFrameworkSample.Products;
using NetFrameworkSample.Lifetimes;
namespace NetFrameworkSample
{
public static class UnityConfig
{
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
container.RegisterType<IProductsService, ProductsService>(TypeLifetime.Singleton);
container.RegisterType<ISecondService, SecondService>(TypeLifetime.Singleton);
container.RegisterType<IThirdService, ThirdService>(TypeLifetime.Singleton);
return container;
});
public static IUnityContainer Container => container.Value;
public static void RegisterTypes(IUnityContainer container)
{ }
}
}
Result
--- Singleton ---
--- Access "ProductsController" ---
2020-10-18 18:58:16.1776||DEBUG|NetFrameworkSample.Lifetimes.ThirdService|ThirdService Constructor |url: http://localhost/|action:
2020-10-18 18:58:16.2536||DEBUG|NetFrameworkSample.Products.ProductsService|ProductsService Constructor |url: http://localhost/|action:
2020-10-18 18:58:16.2536||DEBUG|NetFrameworkSample.Lifetimes.SecondService|SecondService Constructor |url: http://localhost/|action:
2020-10-18 18:58:16.2536||DEBUG|NetFrameworkSample.Controllers.ProductsController|ProductsController Constructor |url: http://localhost/|action:
--- Access "LifetimeController" ---
2020-10-18 18:59:39.8770||DEBUG|NetFrameworkSample.Lifetimes.LifetimeController|LifetimeController Constructor |url: http://localhost/Lifetime|action:
Exclude controller classes, all instances are created only one time.
Scoped
UnityConfig.cs
using System;
using Unity;
using NetFrameworkSample.Products;
using NetFrameworkSample.Lifetimes;
namespace NetFrameworkSample
{
public static class UnityConfig
{
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
container.RegisterType<IProductsService, ProductsService>(TypeLifetime.Scoped);
container.RegisterType<ISecondService, SecondService>(TypeLifetime.Scoped);
container.RegisterType<IThirdService, ThirdService>(TypeLifetime.Scoped);
return container;
});
public static IUnityContainer Container => container.Value;
public static void RegisterTypes(IUnityContainer container)
{ }
}
}
Result
--- Scoped ---
--- Access "ProductsController" ---
2020-10-18 19:05:05.4487||DEBUG|NetFrameworkSample.Lifetimes.ThirdService|ThirdService Constructor |url: http://localhost/|action:
2020-10-18 19:05:05.5336||DEBUG|NetFrameworkSample.Products.ProductsService|ProductsService Constructor |url: http://localhost/|action:
2020-10-18 19:05:05.5336||DEBUG|NetFrameworkSample.Lifetimes.SecondService|SecondService Constructor |url: http://localhost/|action:
2020-10-18 19:05:05.5336||DEBUG|NetFrameworkSample.Controllers.ProductsController|ProductsController Constructor |url: http://localhost/|action:
--- Access "LifetimeController" ---
2020-10-18 19:05:28.1375||DEBUG|NetFrameworkSample.Lifetimes.LifetimeController|LifetimeController Constructor |url: http://localhost/Lifetime|action:
According to the results, the lifetime of "Scoped" is same as "Singleton".
What is "Scoped"?
According to the documents, when the lifetime is set as "Scoped", "Unity returns a unique value for each scope".
Now, I want to know, what is the scope?
According to the "Remarks", "this lifetime creates local singleton for each level of the hierarchy".
And by default, the application has only one container.
So in this sample, "Scoped" lifetime is as same as "Singleton".
How to create instance per request?
I also tried "ContainerControlled", "External", "Hierarchical", "PerContainer", "PerContainerTransient", "PerResolve", and "PerThread".
According to the result, I think I shall choose "PerResolve" to create instance per request.
UnityConfig.cs
using System;
using Unity;
using NetFrameworkSample.Products;
using NetFrameworkSample.Lifetimes;
namespace NetFrameworkSample
{
public static class UnityConfig
{
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
container.RegisterType<IProductsService, ProductsService>(TypeLifetime.PerResolve);
container.RegisterType<ISecondService, SecondService>(TypeLifetime.PerResolve);
container.RegisterType<IThirdService, ThirdService>(TypeLifetime.PerResolve);
return container;
});
public static IUnityContainer Container => container.Value;
public static void RegisterTypes(IUnityContainer container)
{ }
}
}
Result
--- PerResolve ---
--- Access "ProductsController" ---
2020-10-19 00:13:47.1347||DEBUG|NetFrameworkSample.Lifetimes.ThirdService|ThirdService Constructor |url: http://localhost/|action:
2020-10-19 00:13:47.2177||DEBUG|NetFrameworkSample.Products.ProductsService|ProductsService Constructor |url: http://localhost/|action:
2020-10-19 00:13:47.2177||DEBUG|NetFrameworkSample.Lifetimes.SecondService|SecondService Constructor |url: http://localhost/|action:
2020-10-19 00:13:47.2177||DEBUG|NetFrameworkSample.Controllers.ProductsController|ProductsController Constructor |url: http://localhost/|action:
--- Access "LifetimeController" ---
2020-10-19 00:14:09.9652||DEBUG|NetFrameworkSample.Lifetimes.ThirdService|ThirdService Constructor |url: http://localhost/Lifetime|action:
2020-10-19 00:14:09.9672||DEBUG|NetFrameworkSample.Products.ProductsService|ProductsService Constructor |url: http://localhost/Lifetime|action:
2020-10-19 00:14:09.9672||DEBUG|NetFrameworkSample.Lifetimes.SecondService|SecondService Constructor |url: http://localhost/Lifetime|action:
2020-10-19 00:14:09.9672||DEBUG|NetFrameworkSample.Lifetimes.LifetimeController|LifetimeController Constructor |url: http://localhost/Lifetime|action:
But I haven't understand how to use child containers.
So there may be any other way what I shall choose.
Next time, I will try.
Top comments (0)