Closed heikkilamarko closed 1 year ago
I ran into a similar issue in 3.1, I had a ScopedServiceProvider and when HttpClientFactory.CreateClient was called inside it, it caused the next time I resolved a interface some of the instances to be lost. After looking at the source code for I did get it to work setting SupressHandlerScope to true. Not sure what side effects this has. its used to control this logic in the HttpClientFactory - DefaultHttpClientFactory
I confirm that the issue persists even in version 4 of the Azure Functions runtime. Worst of all, I don't use the IHttpClientFactory interface and yet AddScoped always generates new instances on each constructor injection.
Found this thread after banging by head against he wall for 3 days.
We had a transient class A which depended on a scoped dbcontext and another scoped service B with httpclient added through httpclient nuget package and dbcontext
I could reproduce an issue where the first time service A was resolved through a scoped service provider, two instances of dbcontext were created. Service A and Service B receives separate instances of db context.
Service B modified entities through dbcontext and save changes was called on service A. Because there were two instances of dbcontext and each service wrongly had its own instance - nothing was saved to the database.
Subsequent resolutions of service A did not produce the same behavior and both service A and service B received the same instance of dbcontext.
Enabling the feature flag mentioned earlier helped, but I am yet to test it on production.
He everybody.
I see this issue has been opened for almost 3 years. I'm using all the most recent versions and I'm continue facing the same issue. Could we have a permanent solution without workarounds?
@campelo -- are you on v4? Can you please provide an example of what you're seeing in a new issue and tag me and @fabiocav in it? There are a lot of mixed scenarios in this issue and it's getting hard to follow.
I confirm that the issue persists even in version 4 of the Azure Functions runtime. Worst of all, I don't use the IHttpClientFactory interface and yet AddScoped always generates new instances on each constructor injection.
@jorge-duarte -- Without seeing your exact repro, I believe this is working as expected. Each function invocation is its own scope, which mean it gets its own instance of scoped services. Similar to an http request in ASP.NET. Could you also open a separate issue and tag me and @fabiocav in it so we can take a look at your exact scenario?
I also have Scoped lifetime problem with Isolated runtime.
It looks like there are two scoped lifetimes for one http request. One for the "Middlewares" and one for the "Function".
Here is my code:
Startup.cs
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults((IFunctionsWorkerApplicationBuilder configure) =>
{
configure.UseMiddleware<MyMiddleware>();
configure.UseMiddleware<MyMiddleware2>();
})
.ConfigureServices(services =>
{
services.AddScoped<FooService>();
services.AddScoped<BarService>();
})
.Build();
host.Run();
Function1.cs
namespace FunctionApp1
{
public class Function1
{
private readonly FooService _fooService;
private readonly BarService _barService;
public Function1(FooService fooService, BarService barService)
{
_fooService = fooService;
_barService = barService;
}
[Function("Function1")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
_fooService.LogFooId();
_barService.Start();
return req.CreateResponse(HttpStatusCode.OK);
}
}
public class MyMiddleware : IFunctionsWorkerMiddleware
{
private readonly FooService _fooService;
public MyMiddleware(FooService fooService)
{
_fooService = fooService;
}
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
_fooService.LogFooId();
await next(context);
}
}
public class MyMiddleware2 : IFunctionsWorkerMiddleware
{
private readonly FooService _fooService;
public MyMiddleware2(FooService fooService)
{
_fooService = fooService;
}
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
_fooService.LogFooId();
await next(context);
}
}
public class FooService
{
public Guid Id { get; set; } = Guid.NewGuid();
private readonly ILogger _logger;
public FooService(ILogger<FooService> logger)
{
_logger = logger;
}
public void LogFooId()
{
_logger.LogInformation($"FooId: {Id}");
}
}
public class BarService
{
private readonly FooService _fooService;
public BarService(FooService fooService)
{
_fooService = fooService;
}
public void Start()
{
_fooService.LogFooId();
}
}
}
When i call "http://localhost:7071/api/Function1", then it will log this out:
FooId: 2514bc34-f5d6-4833-b14b-5090020479f5 FooId: 2514bc34-f5d6-4833-b14b-5090020479f5 FooId: 3e1a5a61-c90c-4e67-bfc6-3c3151933c34 FooId: 3e1a5a61-c90c-4e67-bfc6-3c3151933c34
Its expected that there should be 1 scoped lifetime in one http request, where the "Middleware" and "Function" scopes are both shared.
So this is a bug too, correct?
Im using:
Core Tools Version: 4.0.4829 Commit hash: N/A (64-bit) Function Runtime Version: 4.11.2.19273
It happens both with .NET 7 rc 2 and .NET 6 with the latest versions of the nuget packages and also the preview packages.
My workout right now, is to instantiate all services in my middleware through the FunctionContext, and then it will work
public class MyMiddleware : IFunctionsWorkerMiddleware
{
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
var fooService = context.InstanceServices.GetRequiredService<FooService>();
fooService.LogFooId();
await next(context);
}
}
public class MyMiddleware2 : IFunctionsWorkerMiddleware
{
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
var fooService = context.InstanceServices.GetRequiredService<FooService>();
fooService.LogFooId();
await next(context);
}
}
Then when i call the api, it will log out:
FooId: 7b36df46-8ec2-41c8-97f7-57a642ac743f
FooId: 7b36df46-8ec2-41c8-97f7-57a642ac743f
FooId: 7b36df46-8ec2-41c8-97f7-57a642ac743f
FooId: 7b36df46-8ec2-41c8-97f7-57a642ac743f
Edit: I have opened this issue in the azure-functions-dotnet-workerproject https://github.com/Azure/azure-functions-dotnet-worker/issues/1133
@Assassinbeast Your issues seems to be in the isolated model. Could you please open a new issue here: https://github.com/Azure/azure-functions-dotnet-worker/issues
Closing this issue as based on the recent reports, the default behavior in 4.0 (or using the EnableEnhancedScopes
) seem to address the problem.
If you continue to experience similar problems, please open a separate issue with the details so we can take a closer look.
Thank you!
Hi,
We are using durable functions with dependency injection. In our
Startup.cs
file, we register our dependencies as follows:In our activity function, we are using normal constructor injection to get
IMyService
instances.The problem is that even if we are using
AddScoped
for registering the service, during the activity function run, each class that asks for the service, gets a different instance ofIMyService
. This breaks our app logic, becauseIMyService
users won't see each other's changes.As a workaround to the earlier Azure Functions runtime DI issues, we had the following pinning in place
FUNCTIONS_EXTENSION_VERSION = 2.0.12673.0
. The pinned version is not supported anymore by Azure, so our function DI is now broken again.