Azure Function - Can you acccess IServiceProvider Before Function Scope? - c#

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.

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.

EF and Multiple Instances of IHostedService

I have multiple IHostedService (BackgroundService) instances registered in my DI container.
services.AddHostedService<ResourceMatchingMessageListeningService>();
services.AddHostedService<ResourceMatchingMessageListeningService2>();
services.AddHostedService<ResourceMatchingMessageListeningService3>();
services.AddHostedService<ResourceMatchingMessageListeningService4>();
services.AddHostedService<ResourceMatchingMessageListeningService5>();
Each service listens to a different Azure Service Bus queue. When receiving a message it writes to an database through EF Core. If I run a single service it works fine, as soon as I run 2 or more I get the error:
A second operation started on this context before a previous operation
completed. This is usually caused by different threads using the same
instance of DbContext.
I'm assuming this error comes up because EF Core is not thread safe and the hosted services all run in different threads.
I tired to fix this by setting EF's service lifetime to be transient), but I still get the same error.
services.AddDbContext<MyDataContext>(options =>
options.UseSqlServer(connectionString, sqlServerOptions =>
sqlServerOptions.CommandTimeout(120)),
ServiceLifetime.Transient, ServiceLifetime.Transient);
Any suggestion on a potential solution here?
You'll need to Create a new service scope for each hosted service, then request your DbContext from that scope.
using var scope = services.CreateScope();
var context = scope.ServiceProvider.GetService<MyDataContext>();
You might want to create a service scope for each message you process. Perhaps even a separate scoped service to handle the processing of each message.
Unfortunately the implementation then starts to look like a service locator anti-pattern.

Instance scope of micro Service in API gateway

I want to change scope of service instance scope from Singleton to Transient(where it create instance every request) using service url but getting compile time error
Below is working code without service url
services.AddTransient(typeof(IUser), typeof(My.UserService));
Below is singleton scope with service url
services.AddSingleton(typeof(IUser), ServiceProxy.Create<IUser>(new Uri("fabric:/My.Microservices/MY.UserService")));
Now I want to add scope as Transient using service url like singleton, how?
Use the generic approach for registering your service, along with a factory delegate
services.AddTransient<IUser>(sp =>
ServiceProxy.Create<IUser>(new Uri("fabric:/My.Microservices/MY.UserService"))
);
In the above code, every time IUser is resolved the delegate will be invoked.

WCF with Castle Windsor error handling to Application Insights

I have the following scenario:
I am using castle windsor component activator to create and destroy the scope for the WCF.
I have a WCFServiceActivator that extends Castles DefaultcomponentActivator and implements the InternalCreate and InternalDestroy to call BeginScope and Scope.Dispose() for the service.
WCF session is defined as per request
I implemented a ErrorHandler class and defined the ProvideFault and HandleError
In the handle error I want to send exception metrics to ApplicationInsights, but when I use castle to provide some factory I get no scope, since it was already destroyed after the provide value runs and the response is sent to the client.
Basically when the HandleError method executes, the scope created by the activator was already destroyed due to the perWcfRequestLifecycle.
I was wondering on maybe creating a new Castle LifecycleStyle that wraps a higher layer of the wcf but I don't know if it is possible.
Does someone have a solution for this scope issue?

ASP.NET MVC 5 + Owin + SimpleInjector

A new asp.net mvc project using owin, webapi, mvc and DI (SimpleInjector) runs fine if I remove the DI lib from the project. However, once introduced, the app blows up when registering the OWIN components for DI. The OWIN startup configuration is being hit and runs without error, but when it comes time to register the dependencies (listed below) I receive the following error:
An exception of type 'System.InvalidOperationException' occurred in Microsoft.Owin.Host.SystemWeb.dll but was not handled in user code
Additional information: No owin.Environment item was found in the context.
SimpleInjector Registration Code:
container.RegisterPerWebRequest<IUserStore<ApplicationUser>>(() => new UserStore<ApplicationUser>());
container.RegisterPerWebRequest<HttpContextBase>(() => new HttpContextWrapper(HttpContext.Current));
// app fails on call to line below...
container.RegisterPerWebRequest(() => container.GetInstance<HttpContextBase>().GetOwinContext());
container.RegisterPerWebRequest(() => container.GetInstance<IOwinContext>().Authentication);
container.RegisterPerWebRequest<DbContext, ApplicationDbContext>();
Update - Full Stack Trace
at
System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase
context) at
WebApplication1.App_Start.SimpleInjectorInitializer.<>c__DisplayClass6.b__2()
in
b:\temp\WebApplication1\WebApplication1\App_Start\SimpleInjectorInitializer.cs:line
41 at lambda_method(Closure ) at
SimpleInjector.Scope.CreateAndCacheInstance[TService,TImplementation](ScopedRegistration2
registration) at
SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration2
registration) at
SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration2
registration, Scope scope) at
SimpleInjector.Advanced.Internal.LazyScopedRegistration2.GetInstance(Scope
scope) at lambda_method(Closure ) at
SimpleInjector.InstanceProducer.GetInstance()
I think the exception is thrown when you call Verify(). Probably at that line, but only when the delegate is called.
Simple Injector allows making registrations in any order and will therefore not verify the existence and correctness of a registration’s dependencies. This verification is done the very first time an instance is requested, or can be triggered by calling .Verify() at the end of the registration process.
I suspect you're registrering the OwinContext only because you need it for getting the IAuthenticationManager.
The problem you face is that the OwinContext is only available when there is a HttpContext. This context is not available at the time the application is build in the composition root. What you need is a delegate which checks the stage of the application and returns a component that matches this stage. You could that by registering the IAuthenticationManager as:
container.RegisterPerWebRequest<IAuthenticationManager>(() =>
AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication);
The delegate will return the Owin controlled IAuthenticationManager when the code runs at 'normal runtime stage' and there is a HttpContext.
But when making an explicit call the Verify() (which is highly advisable to do!) at the end of registration process there is no HttpContext. Therefore we will create a new OwinContext during verifying the container and return the Authentication component from this newly created OwinContext. But only if the container is indeed verifying!
A full and detailed description can be read here as already mentioned in the comments.
Although the question is different, the answer is the same as my answer here.
The problem is that you are injecting HttpContextWrapper into your application and attempting to use its members during application initialization, but at that point in the application lifecycle, HttpContext is not yet available. HttpContext contains runtime state, and it does not make sense to initialize an application within one specific user's context.
To get around this problem, you should use one or more Abstract Factories to access HttpContext at runtime (when it is available) rather than at application initialization, and inject the factories into your services with DI.
Using Ric .Net's answer might work, too, but it will throw an exception every time the application is initialized.
The answer of 'Ric .Net' has pointed me in right direction, but to allow changes to new SimpleInjector, have to change the code as below (as RegisterPerWebRequest is obselete):
container.Register<IAuthenticationManager>(() => AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication, Lifestyle.Scoped);
Also, have to add below two registrations to the container, to allow 'container.Verify()' to work correctly:
container.Register<ApplicationUserManager>(Lifestyle.Scoped);
container.Register<ApplicationSignInManager>(Lifestyle.Scoped);

Categories