From 2c5e7c005bc89e9b3d433b2938fb3cf85017ce71 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 2 Dec 2025 21:07:07 +0000
Subject: [PATCH 1/6] Initial plan
From 861a007819929fdbc49fb5b92f2132a78fcaed14 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 2 Dec 2025 21:18:07 +0000
Subject: [PATCH 2/6] Add TaskEntity support to DurableTaskSourceGenerator
Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
---
src/Abstractions/DurableTaskAttribute.cs | 5 +-
.../AzureFunctions/DurableFunction.cs | 3 +-
src/Generators/DurableTaskSourceGenerator.cs | 99 +++++++--
.../Generators.Tests/ClassBasedSyntaxTests.cs | 206 ++++++++++++++++++
4 files changed, 291 insertions(+), 22 deletions(-)
diff --git a/src/Abstractions/DurableTaskAttribute.cs b/src/Abstractions/DurableTaskAttribute.cs
index c7a6edce..1b7caa4f 100644
--- a/src/Abstractions/DurableTaskAttribute.cs
+++ b/src/Abstractions/DurableTaskAttribute.cs
@@ -8,9 +8,10 @@ namespace Microsoft.DurableTask;
///
///
/// This attribute is meant to be used on class definitions that derive from
-/// or .
+/// , ,
+/// or TaskEntity{TState} from the Microsoft.DurableTask.Entities namespace.
/// It is used specifically by build-time source generators to generate type-safe methods for invoking
-/// orchestrations or activities.
+/// orchestrations, activities, or registering entities.
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class DurableTaskAttribute : Attribute
diff --git a/src/Generators/AzureFunctions/DurableFunction.cs b/src/Generators/AzureFunctions/DurableFunction.cs
index 94925ff8..2f1af7dc 100644
--- a/src/Generators/AzureFunctions/DurableFunction.cs
+++ b/src/Generators/AzureFunctions/DurableFunction.cs
@@ -10,7 +10,8 @@ public enum DurableFunctionKind
{
Unknown,
Orchestration,
- Activity
+ Activity,
+ Entity
}
public class DurableFunction
diff --git a/src/Generators/DurableTaskSourceGenerator.cs b/src/Generators/DurableTaskSourceGenerator.cs
index 32821c93..c45adca1 100644
--- a/src/Generators/DurableTaskSourceGenerator.cs
+++ b/src/Generators/DurableTaskSourceGenerator.cs
@@ -95,7 +95,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
string className = classType.ToDisplayString();
INamedTypeSymbol? taskType = null;
- bool isActivity = false;
+ DurableTaskKind kind = DurableTaskKind.Orchestrator;
INamedTypeSymbol? baseType = classType.BaseType;
while (baseType != null)
@@ -105,13 +105,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
if (baseType.Name == "TaskActivity")
{
taskType = baseType;
- isActivity = true;
+ kind = DurableTaskKind.Activity;
break;
}
else if (baseType.Name == "TaskOrchestrator")
{
taskType = baseType;
- isActivity = false;
+ kind = DurableTaskKind.Orchestrator;
+ break;
+ }
+ else if (baseType.Name == "TaskEntity")
+ {
+ taskType = baseType;
+ kind = DurableTaskKind.Entity;
break;
}
}
@@ -119,13 +125,31 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
baseType = baseType.BaseType;
}
- if (taskType == null || taskType.TypeParameters.Length <= 1)
+ // TaskEntity has 1 type parameter (TState), while TaskActivity and TaskOrchestrator have 2 (TInput, TOutput)
+ if (taskType == null)
{
return null;
}
- ITypeSymbol inputType = taskType.TypeArguments.First();
- ITypeSymbol outputType = taskType.TypeArguments.Last();
+ if (kind == DurableTaskKind.Entity)
+ {
+ // Entity only has a single TState type parameter
+ if (taskType.TypeParameters.Length < 1)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ // Orchestrator and Activity have TInput and TOutput type parameters
+ if (taskType.TypeParameters.Length <= 1)
+ {
+ return null;
+ }
+ }
+
+ ITypeSymbol? inputType = kind == DurableTaskKind.Entity ? null : taskType.TypeArguments.First();
+ ITypeSymbol? outputType = kind == DurableTaskKind.Entity ? null : taskType.TypeArguments.Last();
string taskName = classType.Name;
if (attribute.ArgumentList?.Arguments.Count > 0)
@@ -134,7 +158,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
taskName = context.SemanticModel.GetConstantValue(expression).ToString();
}
- return new DurableTaskTypeInfo(className, taskName, inputType, outputType, isActivity);
+ return new DurableTaskTypeInfo(className, taskName, inputType, outputType, kind);
}
static DurableFunction? GetDurableFunction(GeneratorSyntaxContext context)
@@ -165,9 +189,10 @@ static void Execute(
bool isDurableFunctions = compilation.ReferencedAssemblyNames.Any(
assembly => assembly.Name.Equals("Microsoft.Azure.Functions.Worker.Extensions.DurableTask", StringComparison.OrdinalIgnoreCase));
- // Separate tasks into orchestrators and activities
+ // Separate tasks into orchestrators, activities, and entities
List orchestrators = new();
List activities = new();
+ List entities = new();
foreach (DurableTaskTypeInfo task in allTasks)
{
@@ -175,13 +200,17 @@ static void Execute(
{
activities.Add(task);
}
+ else if (task.IsEntity)
+ {
+ entities.Add(task);
+ }
else
{
orchestrators.Add(task);
}
}
- int found = activities.Count + orchestrators.Count + allFunctions.Length;
+ int found = activities.Count + orchestrators.Count + entities.Count + allFunctions.Length;
if (found == 0)
{
return;
@@ -264,7 +293,8 @@ public static class GeneratedDurableTaskExtensions
AddRegistrationMethodForAllTasks(
sourceBuilder,
orchestrators,
- activities);
+ activities,
+ entities);
}
sourceBuilder.AppendLine(" }").AppendLine("}");
@@ -368,7 +398,8 @@ public GeneratedActivityContext(TaskName name, string instanceId)
static void AddRegistrationMethodForAllTasks(
StringBuilder sourceBuilder,
IEnumerable orchestrators,
- IEnumerable activities)
+ IEnumerable activities,
+ IEnumerable entities)
{
// internal so it does not conflict with other projects with this generated file.
sourceBuilder.Append($@"
@@ -387,11 +418,24 @@ internal static DurableTaskRegistry AddAllGeneratedTasks(this DurableTaskRegistr
builder.AddActivity<{taskInfo.TypeName}>();");
}
+ foreach (DurableTaskTypeInfo taskInfo in entities)
+ {
+ sourceBuilder.Append($@"
+ builder.AddEntity<{taskInfo.TypeName}>();");
+ }
+
sourceBuilder.AppendLine($@"
return builder;
}}");
}
+ enum DurableTaskKind
+ {
+ Orchestrator,
+ Activity,
+ Entity
+ }
+
class DurableTaskTypeInfo
{
public DurableTaskTypeInfo(
@@ -399,19 +443,30 @@ public DurableTaskTypeInfo(
string taskName,
ITypeSymbol? inputType,
ITypeSymbol? outputType,
- bool isActivity)
+ DurableTaskKind kind)
{
this.TypeName = taskType;
this.TaskName = taskName;
- this.InputType = GetRenderedTypeExpression(inputType);
- this.InputParameter = this.InputType + " input";
- if (this.InputType[this.InputType.Length - 1] == '?')
+ this.Kind = kind;
+
+ // Entities only have a state type parameter, not input/output
+ if (kind == DurableTaskKind.Entity)
{
- this.InputParameter += " = default";
+ this.InputType = string.Empty;
+ this.InputParameter = string.Empty;
+ this.OutputType = string.Empty;
}
+ else
+ {
+ this.InputType = GetRenderedTypeExpression(inputType);
+ this.InputParameter = this.InputType + " input";
+ if (this.InputType[this.InputType.Length - 1] == '?')
+ {
+ this.InputParameter += " = default";
+ }
- this.OutputType = GetRenderedTypeExpression(outputType);
- this.IsActivity = isActivity;
+ this.OutputType = GetRenderedTypeExpression(outputType);
+ }
}
public string TypeName { get; }
@@ -419,7 +474,13 @@ public DurableTaskTypeInfo(
public string InputType { get; }
public string InputParameter { get; }
public string OutputType { get; }
- public bool IsActivity { get; }
+ public DurableTaskKind Kind { get; }
+
+ public bool IsActivity => this.Kind == DurableTaskKind.Activity;
+
+ public bool IsOrchestrator => this.Kind == DurableTaskKind.Orchestrator;
+
+ public bool IsEntity => this.Kind == DurableTaskKind.Entity;
static string GetRenderedTypeExpression(ITypeSymbol? symbol)
{
diff --git a/test/Generators.Tests/ClassBasedSyntaxTests.cs b/test/Generators.Tests/ClassBasedSyntaxTests.cs
index 18bd9d32..d0cf51d5 100644
--- a/test/Generators.Tests/ClassBasedSyntaxTests.cs
+++ b/test/Generators.Tests/ClassBasedSyntaxTests.cs
@@ -267,4 +267,210 @@ internal static DurableTaskRegistry AddAllGeneratedTasks(this DurableTaskRegistr
expectedOutput,
isDurableFunctions: false);
}
+
+ [Theory]
+ [InlineData("int")]
+ [InlineData("string")]
+ public Task Entities_PrimitiveStateTypes(string type)
+ {
+ string code = $@"
+#nullable enable
+using System.Threading.Tasks;
+using Microsoft.DurableTask;
+using Microsoft.DurableTask.Entities;
+
+[DurableTask(nameof(MyEntity))]
+class MyEntity : TaskEntity<{type}>
+{{
+ public {type} Get() => this.State;
+}}";
+
+ string expectedOutput = TestHelpers.WrapAndFormat(
+ GeneratedClassName,
+ methodList: @"
+internal static DurableTaskRegistry AddAllGeneratedTasks(this DurableTaskRegistry builder)
+{
+ builder.AddEntity();
+ return builder;
+}");
+
+ return TestHelpers.RunTestAsync(
+ GeneratedFileName,
+ code,
+ expectedOutput,
+ isDurableFunctions: false);
+ }
+
+ [Fact]
+ public Task Entities_CustomStateTypes()
+ {
+ string code = @"
+using System.Threading.Tasks;
+using MyNS;
+using Microsoft.DurableTask;
+using Microsoft.DurableTask.Entities;
+
+[DurableTask(nameof(MyEntity))]
+class MyEntity : TaskEntity
+{
+ public MyClass Get() => this.State;
+}
+
+namespace MyNS
+{
+ public class MyClass { }
+}";
+
+ string expectedOutput = TestHelpers.WrapAndFormat(
+ generatedClassName: "GeneratedDurableTaskExtensions",
+ methodList: @"
+internal static DurableTaskRegistry AddAllGeneratedTasks(this DurableTaskRegistry builder)
+{
+ builder.AddEntity();
+ return builder;
+}");
+
+ return TestHelpers.RunTestAsync(
+ GeneratedFileName,
+ code,
+ expectedOutput,
+ isDurableFunctions: false);
+ }
+
+ [Fact]
+ public Task Entities_ExplicitNaming()
+ {
+ // The [DurableTask] attribute is expected to override the entity class name
+ string code = @"
+using System.Threading.Tasks;
+using MyNS;
+using Microsoft.DurableTask;
+using Microsoft.DurableTask.Entities;
+
+namespace MyNS
+{
+ [DurableTask(""MyEntity"")]
+ class MyEntityImpl : TaskEntity
+ {
+ public MyClass Get() => this.State;
+ }
+
+ public class MyClass { }
+}";
+
+ string expectedOutput = TestHelpers.WrapAndFormat(
+ GeneratedClassName,
+ methodList: @"
+internal static DurableTaskRegistry AddAllGeneratedTasks(this DurableTaskRegistry builder)
+{
+ builder.AddEntity();
+ return builder;
+}");
+
+ return TestHelpers.RunTestAsync(
+ GeneratedFileName,
+ code,
+ expectedOutput,
+ isDurableFunctions: false);
+ }
+
+ [Fact]
+ public Task Entities_Inheritance()
+ {
+ string code = @"
+using System.Threading.Tasks;
+using Microsoft.DurableTask;
+using Microsoft.DurableTask.Entities;
+
+[DurableTask(nameof(MyEntity))]
+class MyEntity : MyEntityBase
+{
+ public override int Get() => this.State;
+}
+
+abstract class MyEntityBase : TaskEntity
+{
+ public abstract int Get();
+}";
+
+ // NOTE: Same output as Entities_PrimitiveStateTypes
+ string expectedOutput = TestHelpers.WrapAndFormat(
+ GeneratedClassName,
+ methodList: @"
+internal static DurableTaskRegistry AddAllGeneratedTasks(this DurableTaskRegistry builder)
+{
+ builder.AddEntity();
+ return builder;
+}");
+
+ return TestHelpers.RunTestAsync(
+ GeneratedFileName,
+ code,
+ expectedOutput,
+ isDurableFunctions: false);
+ }
+
+ [Fact]
+ public Task Mixed_OrchestratorActivityEntity()
+ {
+ // Test that the generator handles a mix of orchestrators, activities and entities
+ string code = @"
+using System.Threading.Tasks;
+using Microsoft.DurableTask;
+using Microsoft.DurableTask.Entities;
+
+[DurableTask(nameof(MyOrchestrator))]
+class MyOrchestrator : TaskOrchestrator
+{
+ public override Task RunAsync(TaskOrchestrationContext ctx, int input) => Task.FromResult(string.Empty);
+}
+
+[DurableTask(nameof(MyActivity))]
+class MyActivity : TaskActivity
+{
+ public override Task RunAsync(TaskActivityContext context, int input) => Task.FromResult(string.Empty);
+}
+
+[DurableTask(nameof(MyEntity))]
+class MyEntity : TaskEntity
+{
+ public int Get() => this.State;
+}";
+
+ string expectedOutput = TestHelpers.WrapAndFormat(
+ GeneratedClassName,
+ methodList: @"
+///
+public static Task ScheduleNewMyOrchestratorInstanceAsync(
+ this IOrchestrationSubmitter client, int input, StartOrchestrationOptions? options = null)
+{
+ return client.ScheduleNewOrchestrationInstanceAsync(""MyOrchestrator"", input, options);
+}
+
+///
+public static Task CallMyOrchestratorAsync(
+ this TaskOrchestrationContext context, int input, TaskOptions? options = null)
+{
+ return context.CallSubOrchestratorAsync(""MyOrchestrator"", input, options);
+}
+
+public static Task CallMyActivityAsync(this TaskOrchestrationContext ctx, int input, TaskOptions? options = null)
+{
+ return ctx.CallActivityAsync(""MyActivity"", input, options);
+}
+
+internal static DurableTaskRegistry AddAllGeneratedTasks(this DurableTaskRegistry builder)
+{
+ builder.AddOrchestrator();
+ builder.AddActivity();
+ builder.AddEntity();
+ return builder;
+}");
+
+ return TestHelpers.RunTestAsync(
+ GeneratedFileName,
+ code,
+ expectedOutput,
+ isDurableFunctions: false);
+ }
}
From 5abe338aed85b3dae047a755a6966eb022e303a0 Mon Sep 17 00:00:00 2001
From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com>
Date: Tue, 2 Dec 2025 22:47:32 -0800
Subject: [PATCH 3/6] func entity sourcegen support
Co-Authored-By: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
---
src/Generators/AzureFunctions/SyntaxNodeUtility.cs | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/Generators/AzureFunctions/SyntaxNodeUtility.cs b/src/Generators/AzureFunctions/SyntaxNodeUtility.cs
index 25d98de5..e5bbdf3f 100644
--- a/src/Generators/AzureFunctions/SyntaxNodeUtility.cs
+++ b/src/Generators/AzureFunctions/SyntaxNodeUtility.cs
@@ -61,6 +61,12 @@ public static bool TryGetFunctionKind(MethodDeclarationSyntax method, out Durabl
kind = DurableFunctionKind.Activity;
return true;
}
+
+ if (attribute.ToString().Equals("EntityTrigger", StringComparison.Ordinal))
+ {
+ kind = DurableFunctionKind.Entity;
+ return true;
+ }
}
}
@@ -125,7 +131,8 @@ public static bool TryGetParameter(
{
string attributeName = attribute.Name.ToString();
if ((kind == DurableFunctionKind.Activity && attributeName == "ActivityTrigger") ||
- (kind == DurableFunctionKind.Orchestration && attributeName == "OrchestratorTrigger"))
+ (kind == DurableFunctionKind.Orchestration && attributeName == "OrchestratorTrigger") ||
+ (kind == DurableFunctionKind.Entity && attributeName == "EntityTrigger"))
{
TypeInfo info = model.GetTypeInfo(methodParam.Type);
if (info.Type is INamedTypeSymbol named)
From 27bb55b3badd55fadc8f4f66dbbd341e03f1d75f Mon Sep 17 00:00:00 2001
From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com>
Date: Wed, 3 Dec 2025 13:12:17 -0800
Subject: [PATCH 4/6] unskip and fix func source gen tests
---
Directory.Packages.props | 2 +-
test/Generators.Tests/AzureFunctionsTests.cs | 103 ++++++++----------
test/Generators.Tests/Generators.Tests.csproj | 2 +
test/Generators.Tests/Utils/TestHelpers.cs | 14 ++-
4 files changed, 59 insertions(+), 62 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index b3945014..42938449 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -11,7 +11,7 @@
-
+
diff --git a/test/Generators.Tests/AzureFunctionsTests.cs b/test/Generators.Tests/AzureFunctionsTests.cs
index 5cfa9d8a..63dd768d 100644
--- a/test/Generators.Tests/AzureFunctionsTests.cs
+++ b/test/Generators.Tests/AzureFunctionsTests.cs
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-//using Microsoft.Azure.Functions.Worker;
+using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask.Generators.Tests.Utils;
namespace Microsoft.DurableTask.Generators.Tests;
@@ -11,7 +11,7 @@ public class AzureFunctionsTests
const string GeneratedClassName = "GeneratedDurableTaskExtensions";
const string GeneratedFileName = $"{GeneratedClassName}.cs";
- [Fact(Skip = "Durable Functions Extension out of date")]
+ [Fact]
public async Task Activities_SimpleFunctionTrigger()
{
string code = @"
@@ -40,7 +40,7 @@ await TestHelpers.RunTestAsync(
isDurableFunctions: true);
}
- [Fact(Skip = "Durable Functions Extension out of date")]
+ [Fact]
public async Task Activities_SimpleFunctionTrigger_TaskReturning()
{
string code = @"
@@ -70,7 +70,7 @@ await TestHelpers.RunTestAsync(
isDurableFunctions: true);
}
- [Fact(Skip = "Durable Functions Extension out of date")]
+ [Fact]
public async Task Activities_SimpleFunctionTrigger_CustomType()
{
string code = @"
@@ -111,7 +111,7 @@ await TestHelpers.RunTestAsync(
///
/// The activity input type.
/// The activity output type.
- [Theory(Skip = "Durable Functions Extension out of date")]
+ [Theory]
[InlineData("int", "string")]
[InlineData("string", "int")]
[InlineData("Guid", "TimeSpan")]
@@ -126,11 +126,18 @@ public async Task Activities_ClassBasedSyntax(string inputType, string outputTyp
using Microsoft.DurableTask;
[DurableTask(nameof(MyActivity))]
-public class MyActivity : TaskActivityBase<{inputType}, {outputType}>
+public class MyActivity : TaskActivity<{inputType}, {outputType}>
{{
- protected override {outputType} OnRun(TaskActivityContext context, {inputType} input) => default!;
+ public override Task<{outputType}> RunAsync(TaskActivityContext context, {inputType} input) => Task.FromResult<{outputType}>(default!);
}}";
+ // Build the expected InputParameter format (matches generator logic)
+ string expectedInputParameter = inputType + " input";
+ if (inputType.EndsWith('?'))
+ {
+ expectedInputParameter += " = default";
+ }
+
string expectedOutput = TestHelpers.WrapAndFormat(
GeneratedClassName,
methodList: $@"
@@ -140,9 +147,9 @@ public class MyActivity : TaskActivityBase<{inputType}, {outputType}>
}}
[Function(nameof(MyActivity))]
-public static async Task<{outputType}> MyActivity([ActivityTrigger] {defaultInputType} input, string instanceId, FunctionContext executionContext)
+public static async Task<{outputType}> MyActivity([ActivityTrigger] {expectedInputParameter}, string instanceId, FunctionContext executionContext)
{{
- ITaskActivity activity = ActivatorUtilities.CreateInstance(executionContext.InstanceServices);
+ ITaskActivity activity = ActivatorUtilities.GetServiceOrCreateInstance(executionContext.InstanceServices);
TaskActivityContext context = new GeneratedActivityContext(""MyActivity"", instanceId);
object? result = await activity.RunAsync(context, input);
return ({outputType})result!;
@@ -164,7 +171,7 @@ await TestHelpers.RunTestAsync(
///
/// The activity input type.
/// The activity output type.
- [Theory(Skip = "Durable Functions Extension out of date")]
+ [Theory]
[InlineData("int", "string?")]
[InlineData("string", "int")]
[InlineData("(int, int)", "(double, double)")]
@@ -183,11 +190,18 @@ public async Task Orchestrators_ClassBasedSyntax(string inputType, string output
namespace MyNS
{{
[DurableTask(nameof(MyOrchestrator))]
- public class MyOrchestrator : TaskOrchestratorBase<{inputType}, {outputType}>
+ public class MyOrchestrator : TaskOrchestrator<{inputType}, {outputType}>
{{
- protected override Task<{outputType}> OnRunAsync(TaskOrchestrationContext ctx, {defaultInputType} input) => throw new NotImplementedException();
+ public override Task<{outputType}> RunAsync(TaskOrchestrationContext ctx, {defaultInputType} input) => throw new NotImplementedException();
}}
}}";
+ // Build the expected InputParameter format (matches generator logic)
+ string expectedInputParameter = inputType + " input";
+ if (inputType.EndsWith('?'))
+ {
+ expectedInputParameter += " = default";
+ }
+
string expectedOutput = TestHelpers.WrapAndFormat(
GeneratedClassName,
methodList: $@"
@@ -200,32 +214,18 @@ public class MyOrchestrator : TaskOrchestratorBase<{inputType}, {outputType}>
.ContinueWith(t => ({outputType})(t.Result ?? default({outputType})!), TaskContinuationOptions.ExecuteSynchronously);
}}
-///
+///
public static Task ScheduleNewMyOrchestratorInstanceAsync(
- this DurableTaskClient client,
- string? instanceId = null,
- {defaultInputType} input = default,
- DateTimeOffset? startTime = null)
+ this IOrchestrationSubmitter client, {expectedInputParameter}, StartOrchestrationOptions? options = null)
{{
- return client.ScheduleNewOrchestrationInstanceAsync(
- ""MyOrchestrator"",
- instanceId,
- input,
- startTime);
+ return client.ScheduleNewOrchestrationInstanceAsync(""MyOrchestrator"", input, options);
}}
-///
+///
public static Task<{outputType}> CallMyOrchestratorAsync(
- this TaskOrchestrationContext context,
- string? instanceId = null,
- {defaultInputType} input = default,
- TaskOptions? options = null)
+ this TaskOrchestrationContext context, {expectedInputParameter}, TaskOptions? options = null)
{{
- return context.CallSubOrchestratorAsync<{outputType}>(
- ""MyOrchestrator"",
- instanceId,
- input,
- options);
+ return context.CallSubOrchestratorAsync<{outputType}>(""MyOrchestrator"", input, options);
}}",
isDurableFunctions: true);
@@ -243,7 +243,7 @@ await TestHelpers.RunTestAsync(
///
/// The activity input type.
/// The activity output type.
- [Theory(Skip = "Durable Functions Extension out of date")]
+ [Theory]
[InlineData("int", "string?")]
[InlineData("string", "int")]
[InlineData("(int, int)", "(double, double)")]
@@ -264,14 +264,21 @@ namespace MyNS
[DurableTask]
public class MyOrchestrator : MyOrchestratorBase
{{
- protected override Task<{outputType}> OnRunAsync(TaskOrchestrationContext ctx, {defaultInputType} input) => throw new NotImplementedException();
+ public override Task<{outputType}> RunAsync(TaskOrchestrationContext ctx, {defaultInputType} input) => throw new NotImplementedException();
}}
- public abstract class MyOrchestratorBase : TaskOrchestratorBase<{inputType}, {outputType}>
+ public abstract class MyOrchestratorBase : TaskOrchestrator<{inputType}, {outputType}>
{{
}}
}}";
// Same output as Orchestrators_ClassBasedSyntax
+ // Build the expected InputParameter format (matches generator logic)
+ string expectedInputParameter = inputType + " input";
+ if (inputType.EndsWith('?'))
+ {
+ expectedInputParameter += " = default";
+ }
+
string expectedOutput = TestHelpers.WrapAndFormat(
GeneratedClassName,
methodList: $@"
@@ -284,32 +291,18 @@ public abstract class MyOrchestratorBase : TaskOrchestratorBase<{inputType}, {ou
.ContinueWith(t => ({outputType})(t.Result ?? default({outputType})!), TaskContinuationOptions.ExecuteSynchronously);
}}
-///
+///
public static Task ScheduleNewMyOrchestratorInstanceAsync(
- this DurableTaskClient client,
- string? instanceId = null,
- {defaultInputType} input = default,
- DateTimeOffset? startTime = null)
+ this IOrchestrationSubmitter client, {expectedInputParameter}, StartOrchestrationOptions? options = null)
{{
- return client.ScheduleNewOrchestrationInstanceAsync(
- ""MyOrchestrator"",
- instanceId,
- input,
- startTime);
+ return client.ScheduleNewOrchestrationInstanceAsync(""MyOrchestrator"", input, options);
}}
-///
+///
public static Task<{outputType}> CallMyOrchestratorAsync(
- this TaskOrchestrationContext context,
- string? instanceId = null,
- {defaultInputType} input = default,
- TaskOptions? options = null)
+ this TaskOrchestrationContext context, {expectedInputParameter}, TaskOptions? options = null)
{{
- return context.CallSubOrchestratorAsync<{outputType}>(
- ""MyOrchestrator"",
- instanceId,
- input,
- options);
+ return context.CallSubOrchestratorAsync<{outputType}>(""MyOrchestrator"", input, options);
}}",
isDurableFunctions: true);
diff --git a/test/Generators.Tests/Generators.Tests.csproj b/test/Generators.Tests/Generators.Tests.csproj
index 61797770..409b69c2 100644
--- a/test/Generators.Tests/Generators.Tests.csproj
+++ b/test/Generators.Tests/Generators.Tests.csproj
@@ -6,6 +6,8 @@
+
+
diff --git a/test/Generators.Tests/Utils/TestHelpers.cs b/test/Generators.Tests/Utils/TestHelpers.cs
index 8b53462d..67f030d9 100644
--- a/test/Generators.Tests/Utils/TestHelpers.cs
+++ b/test/Generators.Tests/Utils/TestHelpers.cs
@@ -3,6 +3,7 @@
using System.Reflection;
using System.Text;
+using Microsoft.Azure.Functions.Worker;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.DependencyInjection;
@@ -38,14 +39,15 @@ public static Task RunTestAsync(
{
// Durable Functions code generation is triggered by the presence of the
// Durable Functions worker extension for .NET Isolated.
- // Assembly functionsWorkerAbstractions = typeof(TriggerBindingAttribute).Assembly;
- // test.TestState.AdditionalReferences.Add(functionsWorkerAbstractions);
+ Assembly functionsWorkerAbstractions = typeof(FunctionAttribute).Assembly;
+ test.TestState.AdditionalReferences.Add(functionsWorkerAbstractions);
- // Assembly functionsWorkerCore = typeof(FunctionContext).Assembly;
- // test.TestState.AdditionalReferences.Add(functionsWorkerCore);
+ Assembly functionsWorkerCore = typeof(FunctionContext).Assembly;
+ test.TestState.AdditionalReferences.Add(functionsWorkerCore);
- // Assembly durableExtension = typeof(OrchestrationTriggerAttribute).Assembly;
- // test.TestState.AdditionalReferences.Add(durableExtension);
+ // OrchestrationTriggerAttribute and ActivityTriggerAttribute are in the DurableTask extension
+ Assembly durableExtension = typeof(OrchestrationTriggerAttribute).Assembly;
+ test.TestState.AdditionalReferences.Add(durableExtension);
Assembly dependencyInjection = typeof(ActivatorUtilities).Assembly;
test.TestState.AdditionalReferences.Add(dependencyInjection);
From 45b5864c8157af30003392c10cbace347c9a1b07 Mon Sep 17 00:00:00 2001
From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com>
Date: Thu, 4 Dec 2025 17:15:06 -0800
Subject: [PATCH 5/6] update build pkg props
---
Directory.Packages.props | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index d8b6f544..6e72d82e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,28 +9,28 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
-
+
@@ -80,10 +80,11 @@
-
+
-
+
+
\ No newline at end of file
From 43b873bc371ba62befca48cad09266d195c5fdb5 Mon Sep 17 00:00:00 2001
From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com>
Date: Thu, 4 Dec 2025 17:31:32 -0800
Subject: [PATCH 6/6] last line
---
Directory.Packages.props | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 6e72d82e..b36897e3 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -86,5 +86,4 @@
-
-
\ No newline at end of file
+
\ No newline at end of file