temporalio / sdk-dotnet

Temporal .NET SDK

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] Action Dependency Injection

m-wild opened this issue · comments

Is there any advice for how to perform dependency injection - particularly services with a Scoped lifetime - with the .NET SDK?

It appears the workflow instance is created from the Type using Activator.CreateInstance(Type).
This works fine for the workflow as it must be deterministic.

However, it appears the responsibility for creating the Activity instance is not part of the SDK, Activity Delegates must be passed to the TemporalWorkerOptions.
For the simple demo code this is fine

var activities = new MyActivities();

using var worker = new TemporalWorker(client,
    new()
    {
        Activities = { activities.SayHello },
        ...
    });

But what if MyActivites has dependencies registered in the DI container with a Scoped lifetime?

The only thing I can think is to manually inject the IServiceProvider into the activity class and handle the service scope manually in user code.

// Worker.cs ----------------
public class Worker : BackgroundService
{
    private readonly IServiceProvider serviceProvider; // from DI container

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var activities = new MyActivities(serviceProvider);

        using var worker = new TemporalWorker(client,
            new()
            {
                Activities = { activities.SayHello },
                ...
            });
    }
}

// MyActivities.cs ----------------
public class MyActivities
{
    private readonly IServiceProvider serviceProvider;

    public MyActivities(IServiceProvider serviceProvider) => this.serviceProvider = serviceProvider;

    [Activity]
    public string SayHello(string name)
    {
        using var scope = serviceProvider.CreateScope();
        var greeter = scope.ServiceProvider.GetRequiredService<IGreeter>();

        return greeter.Greet(name);
    }
}

Does this seem okay?

Is the SDK planning to integrate with Microsoft.Extensions.DependencyInjection?
I could imagine a solution would be passing the IServiceProvider to the TemporalWorkerOptions and letting the SDK manage the activity instance lifecycle, creating and disposing them as required.

using var worker = new TemporalWorker(client,
    new()
    {
        Activities = { typeof(MyActivities) }, // can just pass the Activity type, instance will be created when needed
        ServiceProvider = serviceProvider, // used to resolve Activity instances
        ...
    });

This is an expected feature. Currently, you can use https://github.com/devbased/temporal-activity-gen or do something similar. Additionally, according to @cretz, DI activity can be implemented using Interceptors, although I'm not entirely sure how to do it, and there is no example of that created yet (see: temporalio/samples-dotnet#9).

👍 to what @devbased said. As part of #62 I will be reworking activity definitions so technically a factory can be provided for activity classes (so you can use a service provider to create one) and I will be making the sample reflect that when released (temporalio/samples-dotnet#9 as linked before).

I have created a sample at temporalio/samples-dotnet#16. Review welcome. Still trying to avoid infecting this project with assumptions of DI, but we may be able to do some, see that PR for options.

We now have a dependency injection extension as part of #92.