-
Notifications
You must be signed in to change notification settings - Fork 53
Add AddTasksAsServices extension for registering DurableTask types in DI container #577
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
There was a problem hiding this comment.
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 the AddTasksAsServices extension method to enable DurableTask types (activities, orchestrators, entities) to participate in DI container validation, allowing dependency resolution errors to be detected at startup rather than at runtime.
Key Changes:
- Added internal HashSet collections in
DurableTaskRegistryto track types registered via type-based methods - Implemented
AddTasksAsServicesextension that extracts registered types and registers them as transient services - Added comprehensive test coverage for the new functionality
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Abstractions/DurableTaskRegistry.cs | Added internal HashSet properties to track activity, orchestrator, and entity types |
| src/Abstractions/DurableTaskRegistry.Activities.cs | Modified AddActivity(TaskName, Type) to track type in ActivityTypes collection |
| src/Abstractions/DurableTaskRegistry.Orchestrators.cs | Modified AddOrchestrator(TaskName, Type) to track type in OrchestratorTypes collection |
| src/Abstractions/DurableTaskRegistry.Entities.cs | Modified AddEntity(TaskName, Type) to track type in EntityTypes collection |
| src/Worker/Core/DependencyInjection/DurableTaskWorkerBuilderExtensions.cs | Added AddTasksAsServices extension method with XML documentation and implementation |
| test/Worker/Core.Tests/DependencyInjection/DurableTaskWorkerBuilderExtensionsTests.cs | Added comprehensive test suite including tests for activities, orchestrators, entities, function-based tasks, and worker registration |
| internal HashSet<Type> ActivityTypes { get; } = new HashSet<Type>(); | ||
|
|
||
| /// <summary> | ||
| /// Gets the types of registered orchestrators. | ||
| /// </summary> | ||
| internal HashSet<Type> OrchestratorTypes { get; } = new HashSet<Type>(); | ||
|
|
||
| /// <summary> | ||
| /// Gets the types of registered entities. | ||
| /// </summary> | ||
| internal HashSet<Type> EntityTypes { get; } = new HashSet<Type>(); |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HashSet initialization should use target-typed new() syntax instead of the explicit type for consistency with other similar patterns in the codebase (e.g., GrpcDurableTaskWorkerOptions.cs:32). This follows the C# 9.0 target-typed new expressions pattern that's already used in this codebase.
| internal HashSet<Type> ActivityTypes { get; } = new HashSet<Type>(); | |
| /// <summary> | |
| /// Gets the types of registered orchestrators. | |
| /// </summary> | |
| internal HashSet<Type> OrchestratorTypes { get; } = new HashSet<Type>(); | |
| /// <summary> | |
| /// Gets the types of registered entities. | |
| /// </summary> | |
| internal HashSet<Type> EntityTypes { get; } = new HashSet<Type>(); | |
| internal HashSet<Type> ActivityTypes { get; } = new(); | |
| /// <summary> | |
| /// Gets the types of registered orchestrators. | |
| /// </summary> | |
| internal HashSet<Type> OrchestratorTypes { get; } = new(); | |
| /// <summary> | |
| /// Gets the types of registered entities. | |
| /// </summary> | |
| internal HashSet<Type> EntityTypes { get; } = new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was already addressed in commit b490b9c which changed the HashSet initialization to use target-typed new() syntax.
| internal HashSet<Type> ActivityTypes { get; } = new HashSet<Type>(); | ||
|
|
||
| /// <summary> | ||
| /// Gets the types of registered orchestrators. | ||
| /// </summary> | ||
| internal HashSet<Type> OrchestratorTypes { get; } = new HashSet<Type>(); | ||
|
|
||
| /// <summary> | ||
| /// Gets the types of registered entities. | ||
| /// </summary> | ||
| internal HashSet<Type> EntityTypes { get; } = new HashSet<Type>(); |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HashSet initialization should use target-typed new() syntax instead of the explicit type for consistency with other similar patterns in the codebase (e.g., GrpcDurableTaskWorkerOptions.cs:32). This follows the C# 9.0 target-typed new expressions pattern that's already used in this codebase.
| internal HashSet<Type> ActivityTypes { get; } = new HashSet<Type>(); | |
| /// <summary> | |
| /// Gets the types of registered orchestrators. | |
| /// </summary> | |
| internal HashSet<Type> OrchestratorTypes { get; } = new HashSet<Type>(); | |
| /// <summary> | |
| /// Gets the types of registered entities. | |
| /// </summary> | |
| internal HashSet<Type> EntityTypes { get; } = new HashSet<Type>(); | |
| internal HashSet<Type> ActivityTypes { get; } = new(); | |
| /// <summary> | |
| /// Gets the types of registered orchestrators. | |
| /// </summary> | |
| internal HashSet<Type> OrchestratorTypes { get; } = new(); | |
| /// <summary> | |
| /// Gets the types of registered entities. | |
| /// </summary> | |
| internal HashSet<Type> EntityTypes { get; } = new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was already addressed in commit b490b9c which changed the HashSet initialization to use target-typed new() syntax.
| [Fact] | ||
| public void AddTasksAsServices_DoesNotRegisterFunctionBasedTasks() | ||
| { | ||
| // Arrange | ||
| ServiceCollection services = new(); | ||
| DefaultDurableTaskWorkerBuilder builder = new("test", services); | ||
|
|
||
| // Act | ||
| builder.AddTasksAsServices(registry => | ||
| { | ||
| registry.AddActivityFunc("testFunc", (TaskActivityContext ctx) => Task.CompletedTask); | ||
| }); | ||
|
|
||
| // Assert - No exception should be thrown and no types should be registered | ||
| IServiceProvider provider = services.BuildServiceProvider(); | ||
| // There should be no issue building the service provider | ||
| provider.Should().NotBeNull(); | ||
| } |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a test case for singleton instance registration to verify that instances registered via AddActivity(ITaskActivity) are not added to the DI container, similar to the existing test AddTasksAsServices_DoesNotRegisterFunctionBasedTasks. This would provide complete coverage of the documented behavior that only type-based registrations are included.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added test AddTasksAsServices_DoesNotRegisterSingletonInstances to verify singleton instances are not registered in DI container. Commit: 88c4998
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot address comments |
|
@copilot update pr description to follow .github/PULL_REQUEST_TEMPLATE.md |
Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
Updated PR description to follow the template in SummaryWhat changed?
Why is this change needed?This enables DurableTask types to participate in DI container validation, allowing dependency resolution errors to be detected at startup rather than at runtime. This follows the same pattern as ASP.NET Core's Issues / work itemsProject checklist
AI-assisted code disclosure (required)Was an AI tool used? (select one)
If AI was used:
AI verification (required if AI was used):
TestingAutomated tests
Manual validation (only if runtime/behavior changed)
Notes for reviewers
builder.Services.AddDurableTaskWorker()
.AddTasksAsServices(tasks =>
{
tasks.AddActivity<MyActivity>();
tasks.AddOrchestrator<MyOrchestrator>();
tasks.AddEntity<MyEntity>();
}); |

Summary
What changed?
AddTasksAsServicesextension method onIDurableTaskWorkerBuilderthat registers all task types (activities, orchestrators, entities) as transient services in the DI containerHashSet<Type>collections inDurableTaskRegistryto track types registered via type-based methods (AddActivity(Type),AddOrchestrator(Type),AddEntity(Type))Why is this change needed?
This enables DurableTask types to participate in DI container validation, allowing dependency resolution errors to be detected at startup rather than at runtime. This follows the same pattern as ASP.NET Core's
AddControllersAsServices()extension.Issues / work items
Project checklist
AI-assisted code disclosure (required)
Was an AI tool used? (select one)
If AI was used:
src/Abstractions/DurableTaskRegistry.cs- Added HashSet tracking for registered typessrc/Worker/Core/DependencyInjection/DurableTaskWorkerBuilderExtensions.cs- Added AddTasksAsServices extensiontest/Worker/Core.Tests/DependencyInjection/DurableTaskWorkerBuilderExtensionsTests.cs- Added comprehensive unit tests[]tonew HashSet<Type>()tonew()based on code review feedbackAI verification (required if AI was used):
Testing
Automated tests
Manual validation (only if runtime/behavior changed)
Notes for reviewers
AddActivity<T>(),AddOrchestrator<T>()) are registered in DIAddTasks()internally, so tasks are registered with both DI and the workerOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.