Last active
February 18, 2025 17:14
Revisions
-
dolphinspired revised this gist
Sep 2, 2021 . 1 changed file with 28 additions and 0 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ ## IFunctionContextAccessor Implementation This is a brief tutorial on how to create a dependency-injectable [FunctionContext](https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.functioncontext?view=azure-dotnet) accessor for Azure Functions running on the dotnet-isolated runtime (.NET 5 and up). This will work very similarly to [IHttpContextAccessor](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-5.0) - it will allow you to access details about the current Function invocation and pass arbitrary values between injected services that are scoped to this invocation. 1. Create your interface. You must include both `get` and `set` on this interface. @@ -95,4 +97,30 @@ public static void Main(string[] args) host.Run(); } ``` You're done! You can now inject this accessor into your Functions or injected services. Here's an example: ```csharp public class UserRepository : IUserRepository { private IFunctionContextAccessor FunctionContextAccessor { get; } private ILogger Logger { get; } public UserRepository(IFunctionContextAccessor accessor, ILogger<UserRepository> logger) { FunctionContextAccessor = accessor; Logger = logger; } public async Task<User> GetUserAsync(string userId) { var context = FunctionContextAccessor.FunctionContext; Logger.LogInformation($"Getting users for function invocation: {context.InvocationId}"); context.Items.Add("UserRepositoryAccessed", true); // Idk, return a user or something } } ``` -
dolphinspired revised this gist
Sep 2, 2021 . 1 changed file with 51 additions and 1 deletion.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,6 @@ This is a brief tutorial on how to create a dependency-injectable [FunctionContext](https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.functioncontext?view=azure-dotnet) accessor for Azure Functions running on the dotnet-isolated runtime (.NET 5 and up). This will work very similarly to [IHttpContextAccessor](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-5.0) - it will allow you to access details about the current Function invocation and pass arbitrary values between injected services that are scoped to this invocation. 1. Create your interface. You must include both `get` and `set` on this interface. ```csharp public interface IFunctionContextAccessor @@ -7,6 +9,8 @@ public interface IFunctionContextAccessor } ``` 2. Create an implementation of that interface. This is modeled after the ASP .NET Core implementation of [HttpContextAccessor](https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http/src/HttpContextAccessor.cs) and will allow you to store a FunctionContext instance that's scoped to the current Task chain (i.e. Function invocation). ```csharp public class FunctionContextAccessor : IFunctionContextAccessor { @@ -43,6 +47,52 @@ public class FunctionContextAccessor : IFunctionContextAccessor } ``` 3. Create a middleware to that will set the FunctionContext on each Function invocation. ```csharp public class FunctionContextAccessorMiddleware : IFunctionsWorkerMiddleware { private IFunctionContextAccessor FunctionContextAccessor { get; } public FunctionContextAccessorMiddleware(IFunctionContextAccessor accessor) { FunctionContextAccessor = accessor; } public Task Invoke(FunctionContext context, FunctionExecutionDelegate next) { if (FunctionContextAccessor.FunctionContext != null) { // This should never happen because the context should be localized to the current Task chain. // But if it does happen (perhaps the implementation is bugged), then we need to know immediately so it can be fixed. throw new InvalidOperationException($"Unable to initalize {nameof(IFunctionContextAccessor)}: context has already been initialized."); } FunctionContextAccessor.FunctionContext = context; return next(context); } } ``` 4. Register your accessor and middleware on startup. ```csharp public static void Main(string[] args) { var host = Host.CreateDefaultBuilder() .ConfigureServices((host, services) => { // The accessor itself should be registered as a singleton, but the context // within the accessor will be scoped to the Function invocation services.AddSingleton<IFunctionContextAccessor, FunctionContextAccessor>(); }) .ConfigureFunctionsWorkerDefaults(app => { app.UseMiddleware<FunctionContextAccessorMiddleware>(); }) .Build(); host.Run(); } ``` -
dolphinspired created this gist
Sep 2, 2021 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,48 @@ This is a brief tutorial on how to create a dependency-injectable FunctionContext accessor for Azure Functions running on the dotnet-isolated runtime (.NET 5 and up). ```csharp public interface IFunctionContextAccessor { FunctionContext FunctionContext { get; set; } } ``` ```csharp public class FunctionContextAccessor : IFunctionContextAccessor { private static AsyncLocal<FunctionContextRedirect> _currentContext = new AsyncLocal<FunctionContextRedirect>(); public virtual FunctionContext FunctionContext { get { return _currentContext.Value?.HeldContext; } set { var holder = _currentContext.Value; if (holder != null) { // Clear current context trapped in the AsyncLocals, as its done. holder.HeldContext = null; } if (value != null) { // Use an object indirection to hold the context in the AsyncLocal, // so it can be cleared in all ExecutionContexts when its cleared. _currentContext.Value = new FunctionContextRedirect { HeldContext = value }; } } } private class FunctionContextRedirect { public FunctionContext HeldContext; } } ``` ```csharp ```