I have a complex function, with a rather elaborated object tree it initializes. In order to monitor events in the objects using Application Insights, I need to pass the ExecutionContext.InvocationId
I receive in the function to all classes, because that's what App Insights uses to correlate all events from the same invocation. see Monitor Azure Functions for details. So I wrap the InvocationId
in a class, and I need to pass the same instance around to all of my classes. But since your package resolves instances from the container and not from a LifetimeScope, the default, InstancePerDependency
lifetime is used, and classes are injected with a new instance of the wrapper instead of the one I initialized in the function.
The work around I made was to use reflection to access the private containers
dictionary in DependencyInjection
and call BeginLifetimeScope()
on it. Then, I resolve objects from the scope instead of injecting them to the function with [Inject]
attribute, wrapping most of the function with one big using
block.
My current code looks something along the lines of
[FunctionName("messages")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req,
ILogger log, ExecutionContext context)
{
using (ILifetimeScope scope = BeginLifetimeScope())
{
//we resolve from the container and not inject because we need to control the lifetime scope of the objects.
IApmService apmService = scope.Resolve<IApmService>();
InitializeApmService(apmService, context.InvocationId);
//function code here...
return req.CreateResponse(HttpStatusCode.Accepted);
}
}
private static void InitializeApmService(IApmService apmService, Guid invocationId)
{
apmService.OperationId = invocationId.ToString();
}
private static ILifetimeScope BeginLifetimeScope()
{
FieldInfo containerField = typeof(DependencyInjection).GetField("containers", BindingFlags.Static | BindingFlags.NonPublic);
if (containerField != null && containerField.GetValue(null) is Dictionary<string, IContainer> containers)
{
return containers.TryGetValue(nameof(MainFunction), out IContainer container)
? container.BeginLifetimeScope()
: throw new Exception($"{nameof(MainFunction)} container not found");
}
throw new Exception("Error resolving LifetimeScope");
}
Describe the solution you'd like
Option 1 is to resolve objects from a ILifetimeScope
in DependencyInjection
. The problem with this approach is that the scope should be disposed, and I don't know how you can detect when the function ended execution to call the Dispose()
method.
Option 2 is to expose a BeginLifetimeScope()
method, perhaps accepting a class name (the "functionName" parameter the configuration class constructor should accept), and let the user control the scope.