Create child scope with additional services - c#

With Autofac it pretty easy to create a child scope of the container and register addtional services. How would I achieve the same with the .net's dependency injection?
The IServiceProvider that I tried to inject to a class that needs to create child containers only offers the CreateScope() method that creates an IServiceScope that agian has only the ServiceProvider property and no way to register additional services. Is there anything else I should inject that would allow me to register more services with the container?

I don't know how Autofac works, but I can explain you how Microsoft DI works.
First of all, the Microsoft DI container is designed so that you have two main abstractions:
IServiceCollection: this is the object you use to register services in your applications. Think of this as the builder object for the actual DI container.
ServiceProvider: this is the actual DI container, that you obtain from the IServiceCollection object by invoking the IServiceCollection.BuildServiceProvider extension method. The behavior of this object is described by the IServiceProvider interface (ServiceProvider class implements the IServiceProvider interface)
So working with the DI container is a two steps operation: first of all you need an IServiceCollection object so that you can register services by specifying the implementing type and the lifetime (transient, scoped or singleton), then you can build a ServiceProvider and use it to resolve services in your application. The concrete type actually used for the IServiceCollection interface is the ServiceCollection class.
When you build the service collection in order to get a ServiceProvider instance, you actually get the root container for your application. It is called root container because you can create a hierarchy of service providers having its root in the root container of the application.
Given the root container for your application, in order to create a child container you need to create a scope, which is basically a scope used to resolve services. Each scope has its own container, which is an object implementing the IServiceProvider interface that you can use to resolve services inside of that scope.
This is the general pattern to do that:
// create the IServiceCollection instance
var services = new ServiceCollection();
// register services on the IServiceCollection instance
services.AddSingleton<IFooService, FooService>();
services.AddScoped<IBarService, BarService>();
services.AddTransient<IBuzzService, BuzzService>();
// create the root container
using var rootContaier = services.BuildServiceProvider();
// create a scope
using var scope = rootContainer.CreateScope();
// gets a reference to the container for the scope
var scopeContainer = scope.ServiceProvider;
// use the scope container to resolve services
var fooService = scopeContainer.GetRequiredService<IFooService>();
var barService = scopeContainer.GetRequiredService<IBarService>();
var buzzService = scopeContainer.GetRequiredService<IBuzzService>();
// do whatever you want with the resolved services
fooService.Foo();
barService.Bar();
buzzService.Buzz();
These dependency resolution scopes are really important because they define the lifetimes of the services resolved by using the scope service provider. These are the rules:
services registered with the singleton lifetime are always resolved by the application root container. Even if you use a child container to resolve a singleton service, the actual service resolution is delegated to the root container. Singleton services implementing the IDisposable interface are disposed when the root container is disposed, which usually happens at the application shutdown. Each singleton service is actually resolved once from the root container and the very same instance is reused for the entire lifetime of the application.
services registered with the transient lifetime are basically the opposite of a singleton service. A brand new instance is created each time the service is resolved. Given a scope, each time the scope service provider is used to resolve a transient service a brand new instance of the implementing type is created and tracked by the scope service provider. When the scope is disposed all the transient services that were resolved from that scope and having a disposable implementing type will be disposed too.
services registered with the scoped lifetime behave like singletons for the scope they were created from. If you have a scope and you use the scope service provider to resolve a scoped service, a brand new instance of the implementing type is created only the first time the service is resolved. All the subsequent requests to resolve the same service inside of the same scope will be fulfilled by reusing the instance of the implementing type created the first time the service was resolved in the scope. Scoped services are tracked by the scope: this is useful because a scoped service whose implementing type is disposable, will be disposed when the scope is disposed (same behavior as the transient services).
From the previous rules you can derive a fourth rule: never use the root container to resolve services, always create a scope and resolve services from that scope (remember to dispose the scope when you are done).
You should do that because scoped and transient services are tracked by the scope and disposed when the scope is disposed. If you try to resolve transient and scoped services from the root container they will be tracked by the root container and they will be disposed only when the root container is disposed: this usually happens at the application shutdown, because the lifetime of the root container is basically the application lifetime. Put in other words, you will create a memory leak if you are not compliant with the fourth rule.
Notice that if you use the overload of IServiceCollection.BuildServiceProvider taking a boolean parameter, you can ask the service collection to build a service provider which actually checks that scoped services are never resolved from the root container. This way you will get a partial check for the rule number four (only scoped services are checked for the dangerous resolution from the root container).
Going back to your question, the service registration phase is all done on the very same IServiceCollection instance and is not aware of the concept of service resolution scope. Scopes are only useful in the later stage of the services resolution, to define the lifetime of the resolved services as explained above.
Once you have built the root container from the service collection instance, the service registration phase is done and based on my knowledge you can only resolve services, no additional registrations will be allowed.

Related

Meaning of a Scoped Service in the context of a (non web) host

Before reporting this question as duplicate, this SO Question covers the use of Transient, Scoped, and Singleton, but the answers and discussion target meanings mainly within the context of configuring a WebHost, but my question is relevant to the meaning of these types of services within the context of a (non-web) host, specifically confusion on Scoped Services.
Let's start with the code below:
In Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
I can wrap my head around what Transient and Singleton mean in the non-web host context.
Transient lifetime services are created each time they are requested. (not to be confused with "http request", but to mean literally "each time the service is requested.")
Singleton lifetime services is for the lifetime of the application domain.
But everywhere I read, a Scoped Service in the context of http requests would mean that a new instance is created for each (http?) request. The docs and other discussion is usually geared toward behavior when dealing with a web host, so I am in need of clarification...
What then, is the meaning of Scoped within the context of configuring IServiceCollection on a Default (non-web) HostBuilder?
Host.CreateDefaultBuilder(args)..ConfigureServices((hostContext, services) =>
{
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
});
Also, see here for a practical example.
The closest understanding I came to is "Scoped lifetime services are created once per request (again, not http-request, but request for the service) within the scope. It is equivalent to a singleton in the current scope." ... meaning that if you AddScoped when configuring services on the DefaultBuilder (non-web host), when requested from that same scope, a new, completely separate instance of the service will be generated? Is this ballpark correct?
If I am way off here, I apologize. Thank you for your time.
Yes, in a web host, a lifetime scope is created for the request.
In a console app, windows service, or any type of app, including a web hosted app, you can create a lifetime scope whenever you feel it is needed.
For example you might create a windows service that processes something on a schedule. It might make sense to create a lifetime scope each time the scheduled job is run. Now all components resolved in that scope that are registered as scoped will logically be scoped to it. This will keep your instances separate per run and they will be disposed along with the scope.
This could be useful if you have several different types of task that run independently and asynchronously. This way any component dependencies they have will be scoped properly.

Azure Function - Can you acccess IServiceProvider Before Function Scope?

Hi I have a use case that would be desireable to gain access to IServiceProvider before a function invoke in azure function project.
Attempts:
Using a IWebJobsStartup cs file, I am able to access the serviceCollection before a function invoke. at the end I have attempted this code
var serviceProvider = services.BuildServiceProvider();
serviceProvider.GetRequiredService<IAccountAPITests>();
with in error message "System.InvalidOperationException: 'Unable to resolve service for type 'Microsoft.Azure.WebJobs.Script.IFileLoggingStatusManager' while attempting to activate 'Microsoft.Azure.WebJobs.Script.Diagnostics.HostFileLoggerProvider'.'"
The only time I can access IServiceCollection so far is with in my funciton
public TestAutomationFunction
(
IServiceProvider rootServiceProvider
)
{
_integrationTestService = rootServiceProvider.GetRequiredService<IIntegrationTestService>();
}
This works fine, But the issues is functions are scoped, and thus anything that is resolved with in it will be tied to its same scope.
I did my best to draw a viso of why I dont have this problem in ASP.net and why i do in functions.
I would like to either access IServiceProvider Before a function invoke to ensure my services are not in the same scope. Or after the function is invoked resolve my services with in a spereate scope.
I can of course just change Package X singleton to a scoped, but my use case is not so simple as its deep in my stack i would have to do that all the way down.
This doenst quite answer my question but it makes my question a mute point.
"Do not resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests. It's fine to resolve a singleton service from a scoped or transient service."
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.0#service-lifetimes
I am going to have re work my stack to not allow this to happen, after that I wont have to worry about this issue.

How to inject an IServiceScope into a class in .NET Core

I have a problem with injecting Microsoft.Extensions.DependencyInjection.IServiceScope into my class.
My service implementation:
public class AccountService : IAccountService
{
private readonly IConfiguration _configuration;
private readonly IServiceScope _services;
public AccountService(
IConfiguration configuration,
IServiceScope services) // <-- I can't inject this
{
_configuration = configuration;
_services = services;
}
public async Task CreateAccount(ExternalAccount externalAccount)
{
(some code...)
}
}
In startup.cs:
services.AddTransient<IAccountService, AccountService>();
The problem is that project crashes after above line with following error:
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: IDS.Quickstart.Account.IAccountService Lifetime: Transient ImplementationType: IDS.Quickstart.Account.AccountService': Unable to resolve service for type 'Microsoft.Extensions.DependencyInjection.IServiceScope' while attempting to activate 'IDS.Quickstart.Account.AccountService'.)
---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: IDS.Quickstart.Account.IAccountService Lifetime: Transient ImplementationType: IDS.Quickstart.Account.AccountService': Unable to resolve service for type 'Microsoft.Extensions.DependencyInjection.IServiceScope' while attempting to activate 'IDS.Quickstart.Account.AccountService'.
What is the reason my code refuses to work?
Instead of injecting an IServiceScope, with MS.DI you inject an IServiceProvider. MS.DI will automatically inject a version of the IServiceProvider that is scoped to the current scope. This means that your class can call IServiceProvider.GetService which will lead to the same results as what would happen when you call IServiceScope.ServiceProvider.GetService.
Please be careful with the injection of container-specific abstractions such as IServiceProvider, IServiceScopeFactory, or IServiceScope. If injected into classes that live outside your Composition Root, it leads to the Service Locator anti-pattern. Service Locator comes with quite a few downsides. The name of your class, AccountService, makes me believe that this class lives outside your Composition Root.
Classes that live inside the Composition Root typically consist of solely infrastructure logic (no business logic). Within the Composition Root it is fine to have a dependency on the DI Container (or its abstractions) because this part of the already has a very strong dependency on that particular DI Container.
So if you can, extract the logic that requires to resolve instances from the AccountService class, and move that into a class that consists of solely infrastructure logic, and place it inside the Composition Root.

Lifestyle Mismatches while injecting ISession with SimpleInjector

I have a domain service that has ISession as a dependency in the Ctor.
public JobCreator(IMapper mapper, ISession session)
{
_mapper = mapper;
_session = session;
}
The service is registered as Singleton
container.Register<IKindergardenCreator, KindergardenCreator>(Lifestyle.Singleton);
The ISession however is registered as scoped, since the session should be reopened more than once.
container.Register<ISession>(() => container.GetInstance<ISessionFactory>().OpenSession(),
Lifestyle.Scoped);
When I run the app I get a "Lifestyle mismatch" because of this configuration. What is the right way to configure a singleton service with non singleton service?
What is the right way to configure a singleton service with non singleton service?
The exception information refers to the following documentation
The documentation about lifestyle mismatches explains how to fix violations:
Change the lifestyle of the component to a lifestyle that is as short or shorter than that of the dependency.
Change the lifestyle of the dependency to a lifestyle as long or longer than that of the component.
Instead of injecting the dependency, inject a factory for the creation of that dependency and call that factory every time an instance is required.
Since you can't increase the lifestyle of the ISession dependency, you should either lower the lifestyle of your consuming component (the KindergardenCreator) or inject a factory for ISession instead.
You can lower the lifestyle of your component as follows:
container.Register<IKindergardenCreator, KindergardenCreator>(Lifestyle.Scoped);
You can also change the ISession dependency to Func<ISession> and register this as a factory as follows:
container.RegisterSingleton<Func<ISession>>(container.GetInstance<ISession>);

Unity DI and custom Owin middleware

Custom Owin middleware eventually needs to verify credentials against the database, yet I can't find a way to get instances of repositories or the Asp.Net Identity UserManager from the middleware via DI.
Unfortunately my database driver is a global singleton and should be resolved by the configured Unity container. When debugging, the Unity child container for the request is created after the middleware gets called.
Is there any way to resolve dependencies with Unity in the middleware?
I tried this without success, but the namespace seems to be wrong anyway (System.Web.Mvc vs System.Web.Http)
var userManager = DependencyResolver.Current.GetService<ApplicationUserManager>();
It seems Autofac solves this by creating the request scope as a middleware itself. What about Unity?
UPDATE
This works
var userManager = (ApplicationUserManager)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(ApplicationUserManager));
However the child container (BeginScope of IDependencyResolver) is created later. I registered the UserManager with a HierarchicalLifetimeManager, which probably means it resolves a single instance in the parent container and subsequent requests get the same instance.
What's safer, using a TransientLifetimeManager and just resolve in the parent container, or are there better options?

Categories