Pass Through Data Over IServiceProvider.CreateScope()

Pass Through Data Over IServiceProvider.CreateScope()

Pass Through Data Over IServiceProvider.CreateScope()

[ASP.NET] In some cases you may encounter the situation that you need to pass through some particular data over a new scope of Service Provider.

For instance, when you implement a solution that integrate to a third party that has webhook callbacks. There might be a data from the webhook request that is necessary to be resolved by each service function.

Pass data over IService Provider

The payload of the request contains identity like tenant id. API Controller received the request then it make a request to the services. Most cases we can carry the payload over to the service request via function parameter but for more complex project structure there might be a middleware, filters, service calling another service, attribute, etc. where they should be able to resolve the identifier (like tenant id in this sample) via either a normal dependency injection way or IServiceProvider.GetService().

This might be a rare use case but I post this for anyone who already encounter the same situation and my technique may offer you one more workaround option.


To make sure the necessary data is passed through everywhere from the API controller endpoint throughout all classes that are registered to IServiceCollection.

  1. Create a class that carry the identifier data.

    public class TenantIdentifier
    {
    public string? TenantId { get; set; }
    }
  2. Register this class to IServiceCollection with AddScoped.

    // Register identifier carrier object
    builder.Services.AddScoped<TenantIdentifier>();
  3. Create an extension method for IScopeProvider named CreateServiceScope(). We are going to enforce all developers on the same project to use IScopeProvider.CreateServiceScope() instead of IScopeProvider.CreateScope(). The IScopeProvider.CreateServiceScope() has additional step to transfer TenantId from current scope to the new scope. Also put the extension of the CreateScope() to break the build (ambiguous error) if someone tries to use CreateScope() still. This technique is a workaround when we need to override extension method but we definitely couldn’t by its design.

    namespace PassThroughScopeSample
    {
        public static class ServiceProviderExtension
        {
            public static IServiceScope CreateServiceScope(this IServiceProvider provider)
            {
                var newScope = provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
    
                // Copy tenant identifier values from original scope to the new scope
                var originalTenantIdentifier = provider.GetRequiredService<TenantIdentifier>();
                var newTenantIdentifier = newScope.ServiceProvider.GetRequiredService<TenantIdentifier>();
                newTenantIdentifier.TenantId = originalTenantIdentifier.TenantId;
    
                return newScope;
            }
        }
    }
    
    namespace Microsoft.Extensions.DependencyInjection
    {
        /// <summary>
        /// This is only to interfere and break the build if someone tries to use the standard .NET CreateScope() extension function.
        /// We need to enforce usage of CreateServiceScope() custom extension function to make sure the necessary pass through variables are managed.
        /// </summary>
        public static class ServiceProviderServiceExtensions
        {
            [Obsolete("This will throw error use CreateServiceScope instead.")]
            public static IServiceScope CreateScope(this IServiceProvider provider)
            {
                throw new NotSupportedException("Do not use CreateScope() ");
            }
        }
    }
  4. Time to apply usage of the new extension method.

Add dependency injection of TenantIdentifer class and make sure to set TenantId from the payload when the endpoint is being requested. And whenever you need to create a new scope just use IServiceProvider.CreateServiceScope() instead of IServiceProvider.CreateScope().

```c#
[ApiController]
[Route("[controller]")]
public class WebhookController : ControllerBase
{
    private readonly TenantIdentifier _tenantIdentifier;
    private readonly IServiceProvider _serviceProvider;

    public WebhookController(TenantIdentifier tenantIdentifier, IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
        _tenantIdentifier = tenantIdentifier;
    }

    [HttpPost]
    public async Task Post([FromBody]WebhookPayload webhookPayload)
    {
        _tenantIdentifier.TenantId = webhookPayload.TenantId;
        // Using Task.Run to fire and forget logging operation which is not too important
        _ = Task.Run(async () => // This can be one-liner just expose to explain
        {
            // Create a new scope to avoid scope is disposed sooner than this to be completed
            var scope = _serviceProvider.CreateServiceScope(); // Create a new scope using CreateServiceScope() instead of CreateScope() to carry data
            var logService = scope.ServiceProvider.GetRequiredService<ILogService<WebhookController>>();
            await logService.TenantAuditAsync(webhookPayload.Action);
        });
        await Task.Delay(500); // simmulate actual long-operation
    }
}
```

With this workaround we no longer have to pass through the request identifier via function parameter and worry-free that the service of the new scope will call what other services.

The full (runnable) source code sample can be found at panot-hong/PassThroughScopeSample (github.com)

Related Posts

Awaitable Long Running Producer-Consumer

Awaitable Long Running Producer-Consumer

Normally we use producer-consumer problem to solve certain problems like write buffer, cache buffer or any operation (producer) that needs to entry data into a queue and having another operation (con

read more
Let's create our own Crypto coin, easy and in just a few minutes (no coding knowledge).

Let's create our own Crypto coin, easy and in just a few minutes (no coding knowledge).

Hello everyone who stumbled upon and came to read. I've been away from writing a blog for a while. Caught up with work, trying this and that, blah blah blah. But now it's time to come back and write

read more
Let's try to create Power Automate connected to SQL Server for sending Leave Approval Email using Low code together.

Let's try to create Power Automate connected to SQL Server for sending Leave Approval Email using Low code together.

Hello everyone! I've been away for a long time from blogging due to various events that have kept me busy, making it challenging to find time for writing. In my latest article, I briefly introduced P

read more
Customize the website to display using Tampermonkey

Customize the website to display using Tampermonkey

Many people may feel dissatisfied with certain websites when they browse them, for example:* Disliking intrusive banner advertisements that strain the eyes. * Wishing a website had specific feature

read more
Conditional Formatting (Fx) in PowerBI Custom Visual

Conditional Formatting (Fx) in PowerBI Custom Visual

👋 Hi Everyone 👋 During this time, I have a chance to implement the PowerBI Custom Visual with my team. And we spent a lot of time researching the conditional formatting (Fx) and we found many inter

read more
An Introduction to Microsoft's Power Automate

An Introduction to Microsoft's Power Automate

Today, we're introducing Microsoft's Power Automate, formerly known as Microsoft Flow. If you're familiar with Microsoft Power Platform services such as Dynamics 365, SharePoint, Power Apps, PowerBI,

read more
Write Unit Tests for React Hooks using react-hooks-testing-library

Write Unit Tests for React Hooks using react-hooks-testing-library

Hooks in React are a feature that has drastically changed the way we write React. It's like undergoing plastic surgery in Korea, where some developers love the new look, while others prefer the old o

read more
Scan code with Credential Scanner on Azure DevOps

Scan code with Credential Scanner on Azure DevOps

🥳 Happy New Year 2023! 🥳Wishing everyone a great year ahead!Now, let's get down to business. Today, I'm going to introduce you to a handy tool that checks for leaked passwords, secrets, certifi

read more
Easy way to check user’s permission on SharePoint Online site in the web part

Easy way to check user’s permission on SharePoint Online site in the web part

Hello Everyone! 🎉 Happy New Year 2021 🎉 I hope you have a wonderful holiday, good vibes, and a nice party 🍻. This blog is the 2nd content which’s I write in English. In the previous blog, I explai

read more
Convert interface to enum (for those too lazy to type 'name' in the input form) in TypeScript

Convert interface to enum (for those too lazy to type 'name' in the input form) in TypeScript

![Convert interface to enum cover](/images/posts/transform-interface-as-enum-typescript/transform_interface-as-enum-cover.png)It's a small trick to convert an Interface to an Enum, helping to solve

read more
Utilize WebAssembly in .NET

Utilize WebAssembly in .NET

We heard the WebAssembly quite a while ago but the use case, especially for .NET developers, was still limited. As of the time writing this post, in the last quarter of 2022, there are many new thing

read more
What is SharePoint? How does it work? Let's take a look together! 😆

What is SharePoint? How does it work? Let's take a look together! 😆

Hello everyone who stumbled upon and is reading this content. After spending a considerable time exploring various content on Medium, I wanted to share a little bit of my knowledge. Having delved int

read more