object lifetime using factory method for dependency injection in .net 5 - c#

I'm playing with .net 5 dependency injection, which is very useful. From various sources, like the accepted answer of this question Dependency injection, inject with parameters , I learn that using a factory method in AddTransient/Scoped/Singleton, the container will not dispose the object created, even if the object implements IDisposable. But the following code tells me clearly that the objects are disposed automatically (which is great, but I'd like to understand the story behind).
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ConsoleDI.Example
{
public class TransientDisposable : IDisposable
{
public void Dispose() => Console.WriteLine($"{nameof(TransientDisposable)}.Dispose()");
}
public class ScopedDisposable : IDisposable
{
public void Dispose() => Console.WriteLine($"{nameof(ScopedDisposable)}.Dispose()");
}
public class SingletonDisposable : IDisposable
{
public void Dispose() => Console.WriteLine($"{nameof(SingletonDisposable)}.Dispose()");
}
class Program
{
static void Main(string[] args)
{
using IHost host = CreateHostBuilder(args).Build();
ExemplifyDisposableScoping(host.Services, "Scope 1");
Console.WriteLine();
ExemplifyDisposableScoping(host.Services, "Scope 2");
Console.WriteLine();
host.Run();
}
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
services.AddTransient<TransientDisposable>(_ => new TransientDisposable())
.AddScoped<ScopedDisposable>(_ => new ScopedDisposable())
.AddSingleton<SingletonDisposable>(_ => new SingletonDisposable())
);
static void ExemplifyDisposableScoping(IServiceProvider services, string scope)
{
Console.WriteLine($"{scope}...");
using IServiceScope serviceScope = services.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
_ = provider.GetRequiredService<TransientDisposable>();
_ = provider.GetRequiredService<ScopedDisposable>();
_ = provider.GetRequiredService<SingletonDisposable>();
}
}
}
output:
Scope 1...
ScopedDisposable.Dispose()
TransientDisposable.Dispose()
Scope 2...
ScopedDisposable.Dispose()
TransientDisposable.Dispose()
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /home/zhjun/Work/tmp/ConsoleDI.Example
^Cinfo: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
SingletonDisposable.Dispose()
So why is this? Is this a .net 5 DI container improvement? or is this always this way?

I wouldn't say that the selected answer is incorrect, but at the very least it is incomplete, because the only registration that does not cause instances to be disposed is the AddSingleton<T>(T) call. Everything else (including instances returned from registered factory delegates for transient, scoped, and singleton) will be disposed of. This behavior is 'by design', specified in MS.DI's unit tests, and has been this way since version 1.0. The official Microsoft documentation is quite clear about this:
In the following example, the services are created by the service container and disposed automatically:
services.AddScoped<Service1>();
services.AddSingleton<Service2>();
services.AddSingleton<IService3>(sp => new Service3());
In the following example: [...] The framework doesn't dispose of the services automatically.
services.AddSingleton<Service1>(new Service1());
services.AddSingleton(new Service2());
The central idea behind this is that with AddSingleton<T>(T) you provide an already created instance or, in the terminology of Autofac, 'externally owned'. The supplied instance already lives before the container exists and it might be the user's goal to keep using that object long after the container was disposed of, which is why such instance is not disposed of at that point.

Related

How to use dependency injection for transient instances in async tasks loop

There is a class that implements an interface called SomethingManager.cs
like:
public class SomethingManager : ISomethingManager
This is a worker service in .net 6 and there is another class library project in the same solution that contains the interface and implementation of SomethingManager.
Dependencies are being registered in the worker service project like
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureServices((hostContext, services) =>
{
//AddSingleton or Transient here?
services.AddSingleton<ISomethingManager, SomethingManager>();
...
The problem is that, in the entrypoint project that works in an async way,there is a loop like:
foreach (xml in xmls)
{
tasks.Add(StartProcessAsync(xml));
}
await Task.WhenAll(tasks);
Inside StartProcessAsync it uses SomethingManager instance hat was previously registered and injected in the constructor.
The problem is that the class SomethingManager has some private members that are supposed to be unique for every task and I noticed that in this way it causes fatal errors between the tasks.Actualy this class needs to share a sessionId that a method .Connect() is giving the value every time.We have to call .Connect() method, one time before other actions,inside every task.
So,
My question is how can I register the SomethingManager with Dependency Injection and every task that uses this instance (which is registered with DI) to have different values for its private members?
And, if can't do it in this way, am I supposed to create new instance for this every time?
public Task StartProcessAsync(xmlFileInfo xml)
{
return Task.Run(async () =>
{
//this one doesn't work inside tasks loop it cases problems because
//the sessionId that contains has to be different for every task
//_somethingManager.DoSomething();
//Like this?
var somethingManager= SomethingManager(_someSettings);
somethingManager.DoSomething();
var mem = somethingManager.ThePrivateMember;
//another object which has also private members in the same class.
});
}

Cannot reproduce in tests the exception Cannot consume scoped service from singleton outside Mvc framework

The InvalidOperationException Cannot consume scoped service from singleton is a well-known scenario described very well here
I am investigating a way to reproduce this exception (assuming it comes from the Dependency Injection framework) but I am not succeeding.
I've created a repo with a commit to illustrate it but basically I have the following test:
public class Given_Scoped_Repository_And_Singleton_Service_That_Uses_The_Repository_When_Getting_Service_From_Different_Scope_After_Disposing_First_Scope
: Given_When_Then_Test
{
private IServiceScope _scopeOne;
private IServiceScope _scopeTwo;
private ServiceSample _serviceSampleOne;
private ServiceSample _serviceSampleTwo;
protected override void Given()
{
var serviceCollection =
new ServiceCollection()
.AddScoped<RepositorySample>()
.AddSingleton<ServiceSample>()
.BuildServiceProvider();
_scopeOne = serviceCollection.CreateScope();
_scopeTwo = serviceCollection.CreateScope();
_serviceSampleOne = _scopeOne.ServiceProvider.GetService<ServiceSample>();
_scopeOne.Dispose();
}
protected override void When()
{
_serviceSampleTwo = _scopeTwo.ServiceProvider.GetService<ServiceSample>();
}
[Fact]
public void Then_It_Should_Get_The_Same_Service_Instance()
{
_serviceSampleOne.Should().Be(_serviceSampleTwo);
}
[Fact]
public void Then_It_Should_Have_The_Same_Repository_Instance()
{
_serviceSampleOne.RepositorySample.Should().Be(_serviceSampleTwo.RepositorySample);
}
}
and
class RepositorySample { }
class ServiceSample
{
public RepositorySample RepositorySample { get; }
public ServiceSample(RepositorySample repositorySample)
{
RepositorySample = repositorySample;
}
}
I would expect to see that InvalidOperationException thrown because I am getting a singleton service from a different scope expecting the repository to be different (since it's scoped). These tests are not failing even if I explicitly dispose the first context that instantiated the scoped RepositorySample and I am a bit confused here.
Why the dependency injection framework Microsoft.Extensions.DependencyInjection 3.1.3 doesn't throw the exception alerting me of the singleton-scope trap?
If I dispose the first scope (as if it was a scoped DbContext in an Mvc scenario that should die when a response is issued), shouldn't this cause the RepositorySample instance to be disposed? It isn't, even when I have RepositorySample implement IDisposable I can see the scoped instance is never disposed (the Dispose method is not executed)
How could I design my test to see the exception as if it was happening with a scoped DbContext that is instantiated in a singleton service?
It's not throwing the exception because the option to validate scopes hasn't been specified.
Change this:
.BuildServiceProvider();
to this:
.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });
ServiceProviderOptions.ValidateScopes
true to perform check verifying that scoped services never gets resolved from root provider;
You can also perform the validation when the ServiceProvider is built instead of waiting until a service is resolved:
.BuildServiceProvider(
new ServiceProviderOptions
{
ValidateScopes = true,
ValidateOnBuild = true
});
That's what an MVC app does at startup. That's why the exception is thrown at startup, not when a controller is resolved. It will throw that exception if it detects errors even if the registered dependencies will never be resolved at runtime. To see it, register your dependencies - ServiceSample, RepositorySample - as you did in your unit test, but don't inject them into any controllers. You'll still get the exception at startup when tries to build the ServiceProvider.

Using a Scoped service in a Singleton in an Asp.Net Core app

In my Asp.Net Core App I need a singleton service that I can reuse for the lifetime of the application. To construct it, I need a DbContext (from the EF Core), but it is a scoped service and not thread safe.
Therefore I am using the following pattern to construct my singleton service. It looks kinda hacky, therefore I was wondering whether this is an acceptable approach and won't lead to any problems?
services.AddScoped<IPersistedConfigurationDbContext, PersistedConfigurationDbContext>();
services.AddSingleton<IPersistedConfigurationService>(s =>
{
ConfigModel currentConfig;
using (var scope = s.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
currentConfig = dbContext.retrieveConfig();
}
return new PersistedConfigurationService(currentConfig);
});
...
public class ConfigModel
{
string configParam { get; set; }
}
What you're doing is not good and can definitely lead to issues. Since this is being done in the service registration, the scoped service is going to be retrieve once when your singleton is first injected. In other words, this code here is only going to run once for the lifetime of the service you're registering, which since it's a singleton, means it's only going to happen once, period. Additionally, the context you're injecting here only exists within the scope you've created, which goes away as soon as the using statement closes. As such, by the time you actually try to use the context in your singleton, it will have been disposed, and you'll get an ObjectDisposedException.
If you need to use a scoped service inside a singleton, then you need to inject IServiceProvider into the singleton. Then, you need to create a scope and pull out your context when you need to use it, and this will need to be done every time you need to use it. For example:
public class PersistedConfigurationService : IPersistedConfigurationService
{
private readonly IServiceProvider _serviceProvider;
public PersistedConfigurationService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Foo()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
// do something with context
}
}
}
Just to emphasize, again, you will need to do this in each method that needs to utilize the scoped service (your context). You cannot persist this to an ivar or something. If you're put off by the code, you should be, as this is an antipattern. If you must get a scoped service in a singleton, you have no choice, but more often than not, this is a sign of bad design. If a service needs to use scoped services, it should almost invariably be scoped itself, not singleton. There's only a few cases where you truly need a singleton lifetime, and those mostly revolve around dealing with semaphores or other state that needs to be persisted throughout the life of the application. Unless there's a very good reason to make your service a singleton, you should opt for scoped in all cases; scoped should be the default lifetime unless you have a reason to do otherwise.
Although Dependency injection: Service lifetimes documentation in ASP.NET Core says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But in your case this is not the issue. Actually you are not resolving the scoped service from singleton. Its just getting an instance of scoped service from singleton whenever it requires. So your code should work properly without any disposed context error!
But another potential solution can be using IHostedService. Here is the details about it:
Consuming a scoped service in a background task (IHostedService)
Looking at the name of this service - I think what you need is a custom configuration provider that loads configuration from database at startup (once only). Why don't you do something like following instead? It is a better design, more of a framework compliant approach and also something that you can build as a shared library that other people can also benefit from (or you can benefit from in multiple projects).
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
var persistentConfigBuilder = new ConfigurationBuilder();
var connectionString = builtConfig["ConnectionString"];
persistentStorageBuilder.AddPersistentConfig(connectionString);
var persistentConfig = persistentConfigBuilder.Build();
config.AddConfiguration(persistentConfig);
});
}
Here - AddPersistentConfig is an extension method built as a library that looks like this.
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddPersistentConfig(this IConfigurationBuilder configurationBuilder, string connectionString)
{
return configurationBuilder.Add(new PersistentConfigurationSource(connectionString));
}
}
class PersistentConfigurationSource : IConfigurationSource
{
public string ConnectionString { get; set; }
public PersistentConfigurationSource(string connectionString)
{
ConnectionString = connectionString;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new PersistentConfigurationProvider(new DbContext(ConnectionString));
}
}
class PersistentConfigurationProvider : ConfigurationProvider
{
private readonly DbContext _context;
public PersistentConfigurationProvider(DbContext context)
{
_context = context;
}
public override void Load()
{
// Using _dbContext
// Load Configuration as valuesFromDb
// Set Data
// Data = valuesFromDb.ToDictionary<string, string>...
}
}

Simple Injector, Function Not Being Intercepted?

I am trying to have a cross cutting concern intercept my calls on my controller but for some reason they aren't being intercepted.
I am basically trying to get the example here to work:
http://simpleinjector.readthedocs.org/en/latest/InterceptionExtensions.html
They have some other info in the interception section here too:
http://simpleinjector.readthedocs.org/en/latest/advanced.html
I have a feeling it is because I am not setting up the Container correctly. Could someone show me how I need to change my main to see "Intercepted!!!" after the calls on the Controller are made? Also, can someone tell me if the setup for the Container was wrong, and if so, explain my error(s).
The Code:
static void Main()
{
Console.WriteLine("Start");
RedisController2 redisController = new RedisController2();
Container _container = new Container();
_container.InterceptWith<MonitoringInterceptor>(type => type == typeof(IRedisController2));
_container.RegisterSingle<MonitoringInterceptor>();
redisController.PrintSomething();
redisController.PrintOther();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
internal class MonitoringInterceptor : IInterceptor
{
public MonitoringInterceptor()
{
}
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
//var decoratedType = invocation.InvocationTarget.GetType();
Console.Write("Intercepted!!!");
Console.ReadKey();
}
}
The problem is due to the fact that the Container is not creating the Controller and therefore cannot intercept calls made to it. Try this:
Console.WriteLine("Start");
Container _container = new Container();
_container.Register<IRedisController2, RedisController2>(); // 1
_container.InterceptWith<MonitoringInterceptor>(type =>
type == typeof(IRedisController2));
_container.RegisterSingle<MonitoringInterceptor>();
IRedisController2 redisController =
_container.GetInstance<IRedisController2>(); // 2, 3
redisController.PrintSomething();
redisController.PrintOther();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
Notice that I have:
added code to register the Controller
added code to request an instance of the Controller from the Container
changed the type being created from RedisController2 to IRedisController2 (because IRedisController2 is what has been configured for interception)
Assuming that SimpleInjector is working in the same way as 90% of all IOC containers, it uses RealProxy under the covers* (given the signature it is most likely, although completely against the spirit of SimpleInjector).
Note: The Interception extensions code snippets use .NET’s System.Runtime.Remoting.Proxies.RealProxy class to generate interception proxies. The RealProxy only allows to proxy interfaces. [source]
RealProxy works with interfaces, due to obvious reason (if you think deeply about it for a while). Even if it could work with a base class, that base class must declare its method as virtual for your code to have worked.
Rework your code to use interfaces when calling RedisController2, and remove all references to RedisController2 except at registration.

How does autofac determine lifetime scope on injected parameters

I have been having a nasty Memory Leak issue while using Autofac, which I think I may have resolved. But, I am curious about if the service StatsService injected in the StatsRefreshMessageHandler class is using the lifetime scope of the Helpers class which called it.
Register Service
builder.RegisterType<StatsService>().InstancePerLifetimeScope();
My helpers class is injected with a lifetime scope, it then calls the appropriate message handler. In this example it will be the StatsRefreshMessageHandler
public class Helpers
{
private ILifetimeScope _lifetimeScope;
private ILifetimeScope _lifetimeScope;
public Helpers(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
public void ProcessMessage<T>(T message) where T : class
{
//Voodoo to construct the right message handler type
Type handlerType = typeof(IMessageHandler<>);
Type[] typeArgs = { message.GetType() };
Type constructed = handlerType.MakeGenericType(typeArgs);
//Handle the message
using (var messageScope = _lifetimeScope.BeginLifetimeScope())
{
var handler = messageScope.Resolve(constructed);
var methodInfo = constructed.GetMethod("Handle");
//this is where it call the message handler
methodInfo.Invoke(handler, new[] { message });
}
}
}
And the class (StatsRefreshMessageHandler) below uses standard IOC Injection... But, the question here is where is StatsService resolving from? I assume it is from the lifetime scope of the caller (Helpers), but if it is resolving from the root Kernel, then I still am going to have problems.
public class StatsRefreshMessageHandler : IMessageHandler<StatsRefreshMessage>
{
private readonly StatsService _statsService;
public StatsRefreshMessageHandler(StatsService statsService)
{
_statsService = statsService;
}
public void Handle(StatsRefreshMessage message)
{
_statsService.UpdateStatsCache(DateTime.UtcNow);
Console.WriteLine("DONE STATS");
}
}
There is some detailed documentation about how lifetime scope is determined for objects on the Autofac site. That can probably help clear up some of the questions you may have.
Some quick answers:
The handler you resolve from the messageScope will go in the lifetime scope for which it was registered:
SingleInstance registration will come from the container so it can be shared later with other resolve calls.
InstancePerLifetimeScope and InstancePerDependency will come from the messageScope and will be disposed when `messageScope is disposed.
The StatsService in the constructor of StatsRefreshMessageHandler will also come from messageScope because that's where the handler (the consumer) is being resolved. It will also obey the lifetime scope registration as noted above (e.g., if StatsService is SingleInstance it will end up in the container).

Categories