Skip to content

Instantly share code, notes, and snippets.

@dolphinspired
Last active February 18, 2025 17:14

Revisions

  1. dolphinspired revised this gist Sep 2, 2021. 1 changed file with 28 additions and 0 deletions.
    28 changes: 28 additions & 0 deletions FunctionContextAccessor.md
    Original 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
    }
    }
    ```
  2. dolphinspired revised this gist Sep 2, 2021. 1 changed file with 51 additions and 1 deletion.
    52 changes: 51 additions & 1 deletion FunctionContextAccessor.md
    Original 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 accessor for Azure Functions running on the dotnet-isolated runtime (.NET 5 and up).
    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();
    }
    ```
  3. dolphinspired created this gist Sep 2, 2021.
    48 changes: 48 additions & 0 deletions FunctionContextAccessor.md
    Original 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

    ```