Skip to content

[.NET] Add a Functions-native API for invoking Durable Workflows - #48

Merged
Chris Gillum (cgillum) merged 9 commits into
mainfrom
cgillum-microsoft-reimagined-journey
Jul 28, 2026
Merged

[.NET] Add a Functions-native API for invoking Durable Workflows#48
Chris Gillum (cgillum) merged 9 commits into
mainfrom
cgillum-microsoft-reimagined-journey

Conversation

@cgillum

@cgillum Chris Gillum (cgillum) commented Jul 24, 2026

Copy link
Copy Markdown
Member

.NET-specific fix for #24.

Summary

Azure Functions can now invoke a registered durable workflow using a strongly typed IWorkflowClient, i.e., without an HttpClient or workflow URL:

[Function(nameof(CancelOrderAsync))]
public async Task<IActionResult> CancelOrderAsync(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "orders/{orderId}/cancel")] HttpRequest request,
    string orderId,
    [DurableClient] DurableTaskClient durableClient,
    FunctionContext context)
{
    IWorkflowClient workflows = durableClient.AsWorkflowClient(context);
    IWorkflowRun run = await workflows.RunAsync("CancelOrder", orderId);
    return new AcceptedResult(value: run.RunId, location: null);
}

As shown in the sample above, AsWorkflowClient converts a DurableTaskClient binding parameter to the IWorkflowClient interface, which defines RunAsync and StreamAsync overloads for invoking registered workflows by name.

This PR also fixes Functions-host workflow-result deserialization: host output uses PascalCase, which the source-generated reader previously treated as case-sensitive and could deserialize as null.

Breaking Changes

  • We're adding new methods to the public IWorkflowClient interface, which is a breaking change for external implementers.
  • An untyped null first argument to RunAsync is now ambiguous; callers will need to cast it to Workflow when needed.

Starting a durable workflow from inside an Azure Function required building an
HttpClient and POSTing to the workflow's generated HTTP route, which coupled
function code to that route.

IWorkflowClient already existed, but it was only reachable from the console and
worker host, and every overload required the Workflow object, which function
classes generally don't have a reference to.

Add name-based RunAsync and StreamAsync overloads that resolve against the
registered workflows, and expose the client to function code through
DurableTaskClient.AsWorkflowClient(FunctionContext), mirroring the existing
AsDurableAgentProxy pattern. Starting an unregistered name fails fast with the
new WorkflowNotRegisteredException.

Fixes #24

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 24, 2026 23:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a Functions-native way to invoke Durable Workflows without constructing an HttpClient and calling the generated workflow HTTP endpoints. It does so by extending IWorkflowClient with name-based overloads (with fast-fail when a workflow name isn’t registered) and by exposing an AsWorkflowClient(FunctionContext) extension on DurableTaskClient for Azure Functions code that receives the client via [DurableClient].

Changes:

  • Add name-based RunAsync/StreamAsync overloads to IWorkflowClient, implemented by DurableWorkflowClient using the registered workflow registry.
  • Add DurableTaskClientExtensions.AsWorkflowClient(FunctionContext) to bridge the [DurableClient] binding into an IWorkflowClient in Azure Functions.
  • Demonstrate and validate the feature via the 01_SequentialWorkflow sample plus unit and integration tests.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/DurableTaskClientWorkflowExtensionsTests.cs Adds unit coverage for DurableTaskClientExtensions.AsWorkflowClient and expected error cases.
dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/WorkflowSamplesValidation.cs Extends the existing live-host integration validation to exercise the new “invoke via function code” path.
dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/Workflows/DurableWorkflowClientTests.cs Adds unit coverage for name-based IWorkflowClient overload behavior (success, runId, casing, and error cases).
dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/DurableTaskClientExtensions.cs Introduces AsWorkflowClient(context) extension to construct a workflow client from a [DurableClient] binding and DI options.
dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/CHANGELOG.md Documents the new Azure Functions extension API.
dotnet/src/Microsoft.Agents.AI.DurableTask/Workflows/IWorkflowClient.cs Adds public name-based overloads for running/streaming registered workflows.
dotnet/src/Microsoft.Agents.AI.DurableTask/Workflows/DurableWorkflowClient.cs Implements name-based overloads by resolving workflows from DurableOptions and delegating to existing workflow-object overloads.
dotnet/src/Microsoft.Agents.AI.DurableTask/WorkflowNotRegisteredException.cs Adds a dedicated exception to fail fast on typo’d/unregistered workflow names.
dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md Records the breaking interface change and the new overload surface.
dotnet/samples/DurableWorkflows/AzureFunctions/01_SequentialWorkflow/README.md Documents invoking workflows from function code via AsWorkflowClient (no HttpClient).
dotnet/samples/DurableWorkflows/AzureFunctions/01_SequentialWorkflow/Program.cs Adds explanatory comments pointing to the new sample function usage.
dotnet/samples/DurableWorkflows/AzureFunctions/01_SequentialWorkflow/OrderFunctions.cs Adds sample HTTP-trigger functions that start a registered workflow via AsWorkflowClient.
dotnet/samples/DurableWorkflows/AzureFunctions/01_SequentialWorkflow/demo.http Adds demo requests for the new sample endpoints.
Comments suppressed due to low confidence (2)

dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/DurableTaskClientWorkflowExtensionsTests.cs:53

  • CreateServiceProviderWithWorkflows() returns a disposable ServiceProvider, but it’s created inline here and never disposed. Prefer creating it in a using declaration so the container is cleaned up after the test.
        FunctionContext context = CreateContext(CreateServiceProviderWithWorkflows());

dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/DurableTaskClientWorkflowExtensionsTests.cs:105

  • This test creates a disposable ServiceProvider inline via CreateServiceProviderWithWorkflows() and never disposes it. Use a using variable to ensure the container is disposed at the end of the test.
        FunctionContext context = CreateContext(CreateServiceProviderWithWorkflows());

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 24, 2026 23:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Comment thread dotnet/src/Microsoft.Agents.AI.DurableTask/Workflows/DurableWorkflowClient.cs Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 25, 2026 00:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 25, 2026 00:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

The Azure Functions worker serializes orchestration output with the Durable Task default JsonDataConverter, which applies no naming policy and writes PascalCase. DurableWorkflowJsonContext read it back with a camelCase, case-sensitive source-generated context, so every property silently bound to null and the workflow result was lost. The built-in HTTP endpoint was unaffected because it round-trips through the same converter via ReadOutputAs<T>().

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 25, 2026 00:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

@cgillum
Chris Gillum (cgillum) marked this pull request as ready for review July 25, 2026 03:09
Copilot AI review requested due to automatic review settings July 27, 2026 16:47
@cgillum Chris Gillum (cgillum) changed the title Add a Functions-native API for invoking Durable Workflows [.NET] Add a Functions-native API for invoking Durable Workflows Jul 27, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

@cgillum Chris Gillum (cgillum) added the breaking-change A breaking change is being introduced by this PR label Jul 27, 2026
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 27, 2026 17:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 27, 2026 22:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 27, 2026 22:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

@cgillum
Chris Gillum (cgillum) merged commit 3ac4399 into main Jul 28, 2026
9 checks passed
@cgillum
Chris Gillum (cgillum) deleted the cgillum-microsoft-reimagined-journey branch July 28, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change A breaking change is being introduced by this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants