Help with Running Temporal Workers on ASP.NET (Core) Web Application

Hi,
I am currently building an AspNet (Core) website which aims to trigger long running workflows. I tried following the examples here, however, I am having a hard time in getting things to run, so any insight on what I am doing wrong would be greatly appreciated.

Problem
When triggered the workflow, nothing happens. I get no exceptions, no logs. Sometimes I see something like so dumped onto the server’s console:

{"level":"info","ts":"2023-10-08T13:16:59.095+0200","msg":"none","service":"matching","component":"matching-engine","wf-task-queue-name":"/_sys/MyWorkflow/3","wf-task-queue-type":"Activity","wf-namespace":"novum.local","lifecycle":"Started","logging-call-at":"task_queue_manager.go:331"}

Setup

  • OS: Windows 10
  • Nuget Packages: Temporalio (0.1.0-beta1) and Temporalio.Extensions.Hosting(0.1.0-beta1)
  • Tried with both development temporal setup (./temporal server start-dev) and also standalone temporal server.The outcome is the same.

What I have

  1. I created a TemporalClientProvider, which wraps around the TemporalClient.ConnectAsync(...). This is set to exist as a singleton.
  2. I am calling the AddHostedTemporalWorker, as part of my bootstrapping setup; registering activities and workflows as I go along:
services.AddHostedTemporalWorker(
                temporalConfiguration.TargetHost,
                temporalConfiguration.NameSpace, MyWorkflow.WorkflowQueueName)
            .AddScopedActivities<MyActivity>()
            .AddWorkflow<MyWorkflow>();
  1. I am defining my activities as so:
public class MyActivity
{
    private readonly IDependency _dependency;

    // Parameterless constructor to be used only for deserialization purposes.
    public MyActivity()
    {
        //
    }
    
    public MyActivity(IDependency dependency)
    {
        _dependency = dependency;
    }

    [Activity("My Activity")]
    public async Task DoSomething(MyWorkflowContext ctx)
    {
        _dependency.DoSomething();
    }
}
  1. I am defining my workflows as so (the file has a .workflow.cs extension):
public class MyWorkflow
{
    private const string TaskQueue = nameof(MyWorkFlow);
    private readonly ITemporalClientProvider _temporalClientProvider;
    private static readonly RetryPolicy DefaultRetryPolicy = new()
    {
        MaximumAttempts = 5
    };

    // Parameterless constructor to be used only for deserialization purposes.
    public MyWorkflow()
    {
        //
    }

    public MyWorkflow(ITemporalClientProvider temporalClientProvider)
    {
        _temporalClientProvider = temporalClientProvider;
    }
    
    public const string WorkflowQueueName = $"{nameof(MyWorkflow)}";

    public async Task ExecuteAsync(MyWorkflowContext ctx)
    {
        ITemporalClient temporalClient = await _temporalClientProvider.ConnectAsync();
        await temporalClient.ExecuteWorkflowAsync<MyWorkflow>(
            myWorkFlow=> myWorkFlow.RunAsync(ctx),
            new WorkflowOptions(ConstructWorkflowId(ctx), TaskQueue));
    }

    [WorkflowRun]
    public override async Task RunAsync(MyWorkflowContext ctx)
    {
        await Workflow.ExecuteActivityAsync<MyActivity>(activity=>
            activity.DoSomething(ctx),
        new ActivityOptions
        {
            CancellationToken = ctx.CancellationToken,
            RetryPolicy = DefaultRetryPolicy,
            ActivityId = $"{ctx.TenantId}_{typeof(MyActivity).FullName!}_{DateTime.UtcNow:HH:mm:ss_dd-MM-yy})",
            TaskQueue = TaskQueue,
        });
    }
  1. Lastly, I am calling (from another IHostedService) the workflow like so:
await _myWorkflow..ExecuteAsync(new MyWorkflowContext()
    {
        ...
    });

What’s happening

  • The RunAsync(...) in step 4 is not being called at all. I tried with/without arguments and various other permutations of the entire setup, but to no avail.

Any information on what I am doing wrong is appreciated.

A workflow is a standalone set of deterministic code, it should not have client calls within it. Also, your workflow doesn’t have a [Workflow] attribute (which should fail registration). Also, unsure why there are separate constants for TaskQueue and WorkflowQueueName, but usually the task queue is not the same name as the workflow.

Regardless, I would have to understand how/where you are starting the worker (where are you running the generic host or ASP host) and how/where you are starting the workflow. Does this sample work for you? Can you provide a full standalone replication with all of the code in place? If necessary you can just alter that sample showing how yours is not working.

Hi Chad,
Thanks for getting back to me so soon, and apologies for taking so long to respond.
Indeed, the workflow I actually have has the [Workflow] class attribute, that was an error from my side when copying things around.

I was following the example you have linked, and also trying to mix and match the one which highlights the usage of workflows and activities. My original assumption was that I was missing something…

I managed to get the minimal example working (somehow), so I will look into this again and compare what’s in the minimal example with the full solution and report back my findings. I will also keep in mind the points you made with regards to queue names.

1 Like