skip to Main Content

I have a tricky requirement where I need create copy of a service that has been created via Constructor DI in my Azure Function

public MyFunction(IMyService myService,
             IServiceProvider serviceProvider, 
             ServiceCollectionContainer serviceCollectionContainer) 
{
    _myService = tmToolsService;
    _serviceProvider = serviceProvider;
    _serviceCollectionContainer = serviceCollectionContainer;
}

[FunctionName("diagnostic-orchestration")]
public async Task DiagnosticOrchestrationAsync(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{

}
    

This service has a lot of dependencies so I dont really want go down the manual Activator.CreateInstance route

I have tried 2 different approaches

Approach 1

I have ServiceCollectionContainer. This is filled in Configure of the startup and simply holds the services

public override void Configure(IFunctionsHostBuilder builder)
{
   base.Configure(builder);

   var services = builder.Services; 
   services.AddSingleton(s => new ServiceCollectionContainer(services));
}

In my function I call

var provider = _serviceCollectionContainer.ServiceCollection.BuildServiceProvider();
if (provider.GetService<IMyService>() is IMyService myService)
{
   await myService.MyMathodAsync();
}

This throws the error

System.InvalidOperationException: 'Unable to resolve service for type 
'Microsoft.Azure.WebJobs.Script.IEnvironment' while attempting to activate
'Microsoft.Azure.WebJobs.Script.Configuration.ScriptJobHostOptionsSetup'.'

I believe this could be because although the service collection looks fine (276 registered services) I have seen references online that say that Configure may be unreliable

Approach 2

The second approach is the more conventional one, I just tried to use the service provider injected without making any changes

if (_serviceProvider.GetService<IMyService>() is IMyService myService)
{
   await myService.MyMathodAsync();
}

But if I use this approach I get the error

‘Scope disposed{no name} is disposed and scoped instances are disposed and no longer availab

How can I fix this?

I have large date range of data that I am processing. I need to split my date range and use my service to process each date range. My service has repositories. Each repository has a DbContext. Having each segment of dates run in the context of its own service allows me to run the processing in parallel without having DbContext queries being run in parallel which causes issues with Ef Core

This processing is running inside a durable function

2

Answers


  1. I don’t know if this holds true for Azure Functions and moreover I am not experienced with durable ones, though as it seems that the main goal is to run parallel queries via ef core through your IMyService then you could in the constructor:

    public MyFunction(IServiceScopeFactory serviceScopeFactory) 
    {
      _serviceScopeFactory = serviceScopeFactory;
    }
    

    And then in the function call, assuming you have an IEnumerable "yourSegments" of the things you want to process in parallel:

    var tasks = yourSegments.Select(async segment =>
            {
                using (var scope = _serviceScopeFactory.CreateScope())
                {
                    var IMyService = scope.ServiceProvider.GetRequiredService<IMyService>();
                    await IMyService.MyMathodAsync(segment);
    
    
                }
            });
    
     await Task.WhenAll(tasks);
    

    I got this from a nice blog post that explains "Since we project our parameters to multiple tasks, each will have it’s own scope which can resolve it’s own DbContext instance."

    Login or Signup to reply.
  2. You can create a 1:1 copy by using this extension method.
    It is a large function, to large for SO, so I’ve put a pastebin here.
    https://pastebin.com/1dKu01w9

    Just call _myService.DeepCopyByExpressionTree(); within your constructor.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search