Reputation: 217
I am using Azure Functions v3 and DI. I have a service that I setup with a transient lifetime in my StartUp.cs (using: builder.Services.AddTransient<ICoreApiClient>(s => coreApiService);
) and then I inject into my Azure Function class. The Azure Function is a Storage Queue trigger function.
I expect that for each queue message I will get a separate instance of my "coreApiService" but I seem to be sharing one instance when multiple messages are put in the queue at the same time.
Is my expectation to get a separate instance for each invocation or "Run" incorrect? The issue I am running into is that the "_coreApiClient" seems to be shared between multiple invocations of the Run method so as I set properties within it (like "Customer ID" or "API Key", etc.) that I are valid for one message and should remain for the processing of that message are changing as it starts processing the next message.
Am I doing it wrong or do I not understand the lifetime properly?
Here is my startup code:
public class Startup: FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddLogging();
var loggingService = new HttpLoggingService(Environment.GetEnvironmentVariable("LoggingURL"), 0, "ABC.Integrations");
builder.Services.AddTransient<ILoggingService>(s => loggingService);
int CoreApiTimeout = 60; //Environment.GetEnvironmentVariable("CoreApiTimeout")
var coreApiService = new CoreApiClient(Environment.GetEnvironmentVariable("CoreApiKey"),string.Empty, CoreApiTimeout,Environment.GetEnvironmentVariable("CoreApiBaseUrl"));
builder.Services.AddTransient<ICoreApiClient>(s => coreApiService);
}
}
And my Azure Function class:
public class TaskRouter
{
private readonly ICoreApiClient _coreApiClient;
private readonly ILoggingService _upgLogger;
private readonly ILogger<TaskRouter> _log;
public TaskRouter(ICoreApiClient coreApiClient, ILoggingService upgLogger, ILogger<TaskRouter> log)
{
_coreApiClient = coreApiClient;
_upgLogger = upgLogger;
_log = log;
}
[FunctionName("RouteTask")]
[ExponentialBackoffRetry(5, "00:00:04", "00:03:00")]
public async Task Run([QueueTrigger("task-scheduler", Connection = "")]
string queueItem)
{
//Call Appropriate Function to start task
var taskMsg = JsonConvert.DeserializeObject<TaskMessage>(queueItem);
_coreApiClient.SetId(taskMsg.TaskNumber);
//DO SOME WORK HERE
Console.Log(_coreApiClient.GetId); //Will be different b/c this was set by the next message
}
}
Upvotes: 1
Views: 934
Reputation: 247551
Although the client is registered as transient with the container, it is actually a singleton since the instance created in Startup is the only instance that will ever be returned for each call to inject ICoreApiClient
.
Move the instance creation into the factory delegate, making this an actual transient registration, so that a new instance will be initialized each time ICoreApiClient
has to be resolved from the service provider.
//...
builder.Services.AddTransient<ICoreApiClient>(s =>
new CoreApiClient(Environment.GetEnvironmentVariable("CoreApiKey"), string.Empty, CoreApiTimeout, Environment.GetEnvironmentVariable("CoreApiBaseUrl"))
);
//...
Upvotes: 1