Last active
May 6, 2023 01:44
-
-
Save fatihmemis/bd1e9853a3b454788395a8921596d974 to your computer and use it in GitHub Desktop.
Service lifetime samples for Asp.Net Core dependency injection.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static void Run() | |
{ | |
var serviceBuilder = new ServiceCollection(); | |
serviceBuilder | |
.AddSingleton<SingletonDisposableService>() | |
.AddTransient<TransientDisposableService>() | |
.AddScoped<ScopedDisposableService>() | |
; | |
// You should use validateScopes: true as a best practice. It's set to false here for demo purposes. | |
using var services = serviceBuilder.BuildServiceProvider(validateScopes: false); | |
var logger = new TestLogger(); | |
// Tests in Scope | |
logger.Header("Creating a service scope..."); | |
using (var scope = services.CreateScope()) | |
{ | |
// Resolve scoped service twice and compare reference | |
// Resolve scoped service first time | |
logger.Header("Resolving first scoped service in service scope..."); | |
var scopedService1 = scope.ServiceProvider.GetService<ScopedDisposableService>(); | |
scopedService1.SayHello(); | |
logger.Separator(); | |
// Resolve scoped service second time | |
logger.Header("Resolving second scoped service in service scope..."); | |
var scopedService2 = scope.ServiceProvider.GetService<ScopedDisposableService>(); | |
scopedService2.SayHello(); | |
logger.Separator(); | |
// Compare references of two scope services and print the result | |
var scopedServicesAreSame = Object.ReferenceEquals(scopedService1, scopedService2); | |
logger.Log($"{nameof(scopedService1)} { (scopedServicesAreSame ? "==" : "!=") } {nameof(scopedService2)}"); | |
// Resolve transient service | |
logger.Header("Resolving transient service in service scope..."); | |
var transientInScope = scope.ServiceProvider.GetService<TransientDisposableService>(); | |
transientInScope.SayHello(); | |
logger.Separator(); | |
// Resolve sigleton service | |
logger.Header("Resolving singleton service in service scope..."); | |
var singletonInScope = scope.ServiceProvider.GetService<SingletonDisposableService>(); | |
singletonInScope.SayHello(); | |
logger.Separator(); | |
} | |
logger.Log("Service scope disposed.", spaceAfter: true); | |
// Resolve tests in root scope | |
logger.Header("Testing in root scope"); | |
// Singleton service | |
logger.Log("Resolving singleton service in ROOT scope..."); | |
var singletonService = services.GetService<SingletonDisposableService>(); | |
singletonService.SayHello(); | |
logger.Separator(); | |
// Transient service | |
logger.Log("Resolving transient service in ROOT scope..."); | |
var transientService = services.GetService<TransientDisposableService>(); | |
transientService.SayHello(); | |
logger.Separator(); | |
// Scoped service | |
logger.Log("Resolving scope service in ROOT scope..."); | |
// If validateScopes parameter is true, [ ex: serviceBuilder.BuildServiceProvider(validateScopes: true) ] | |
// This line will throw an exception, because scope services will not be allowed in the root scope. | |
// In this test we used validateScopes: false for demo purposes. | |
var scopedService = services.GetService<ScopedDisposableService>(); | |
scopedService.SayHello(); | |
logger.Separator(); | |
logger.Header("Disposing services..."); | |
// Although we used "using..." keyword as a best practice while building services, we explicitly dispose it for demo purposes | |
services.Dispose(); | |
logger.Log("Services disposed."); | |
logger.Separator(); | |
} | |
public class ScopedDisposableService : DisposableServiceBase | |
{ | |
public ScopedDisposableService(TransientDisposableService transientDisposableService, SingletonDisposableService singletonDisposableService) | |
{ | |
TransientDisposableService = transientDisposableService; | |
SingletonDisposableService = singletonDisposableService; | |
} | |
public TransientDisposableService TransientDisposableService { get; } | |
public SingletonDisposableService SingletonDisposableService { get; } | |
} | |
public class SingletonDisposableService : DisposableServiceBase { } | |
public class TransientDisposableService : DisposableServiceBase { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Container tracks instance if service is registered by implemented type. | |
services.AddSingleton<IMyDisposableSingletonService, MyDisposableSingletonService>(); | |
// Container tracks instance if service is registered by factory function. | |
services.AddSingleton<IMyDisposableSingletonService>(sp => new MyDisposableSingletonService()); | |
// Container does NOT track if service is registered by instance | |
services.AddSingleton<IMyDisposableSingletonService>(new MyDisposableSingletonService()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IMyDisposableTransientServiceFactory | |
{ | |
IMyDisposableTransientService CreateNew(IServiceProvider services); | |
} | |
// Usage | |
class MyService | |
{ | |
IServiceProvider _services; | |
IMyDisposableTransientServiceFactory _transientServiceFactory; | |
public MyService(IServiceProvider services, IMyDisposableTransientServiceFactory transientServiceFactory) | |
{ | |
// Code omitted | |
} | |
public void DoSomeStuff(IEnumerable<TaskNotification> tasks) | |
{ | |
foreach (var task in tasks) { | |
using var transientService = _transientServiceFactory.CreateNew(_services); | |
// ... | |
} | |
// or create a new scope for the batch operation for dependent services used by IMyDisposableTransientService | |
using var scope = _services.GetRequiredService<IServiceScopeFactory>().CreateScope(); | |
foreach (var task in tasks) { | |
using var transientService = _transientServiceFactory.CreateNew(scope.ServiceProvider); | |
// ... | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// If you need faster application warmups | |
// instead of registering startup services by type | |
// services.AddTransient<MyService>(); | |
// Register them by service factory | |
services.AddTransient<MyService>(sp => new MyService(sp.GetRequiredService<IMyDependencyService>())); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static class Program { | |
static void Main() { | |
var services = new ServiceCollection(); | |
services.AddTransient<TransientService>(); | |
services.AddScoped<ScopedService>(); | |
using ServiceProvider serviceProvider = services.BuildServiceProvider(); | |
// Since it is resolved in root scope, Scoped service will act like a singleton | |
// Transient service which is a dependency of ScopedService will also be not disposed until the service provider is disposed. | |
var avoidToResolveLikeThisScopedService = serviceProvider.GetService<ScopedService>(); | |
// Correct usage | |
using (var scope = serviceProvider.CreateScope()) { | |
var resolvedScopedService = scope.ServiceProvider.GetService<ScopedService>(); | |
// Do something with the resolved service | |
} | |
// ... some other code | |
} | |
} | |
// ... | |
sealed class TransientService : IDisposable { | |
public void Dispose() {} | |
} | |
class ScopedService { | |
public ScopedService(TransientService transientService){ | |
// ... | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static class Program { | |
static void Main() { | |
var services = new ServiceCollection(); | |
services.AddSingleton<SingletonService>(); | |
services.AddScoped<ScopedService>(); | |
using ServiceProvider serviceProvider = services.BuildServiceProvider(validateScopes: true); | |
// This will cause an exception | |
var resolvedSingletonService = serviceProvider.GetService<SingletonService>(); | |
} | |
} | |
class SingletonService { | |
public SingletonService(ScopedService scopedService) | |
{ | |
// ... | |
} | |
} | |
class ScopedService {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyService { | |
public MyService(IServiceProvider services) { | |
this._logger = services.GetRequiredService<ILogger<MyService>>(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyService { | |
public MyService() { | |
this._logger = ServiceLocator.Services.GetRequiredService<ILogger<MyService>>(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyService { | |
public MyService(ILogger<MyService> logger) { | |
this._logger = logger; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ApiController] | |
public class EventsController: ConrollerBase { | |
public EventsController(ISomeCommonService commonService) { | |
_commonService = commonService; | |
} | |
[HttpGet("{id}")] | |
public ActionResult<EventModel> Get(string id){ | |
// ... code using ISomeCommonService | |
} | |
[HttpPost("uncommon-action")] | |
public ActionResult UncommonAction([FromServices]IPrivateService privateService, [FromBody]MyData data){ | |
// ... code using IPrivateService | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment