-
Notifications
You must be signed in to change notification settings - Fork 53
Add dependency injection support to DurableTaskTestHost #613
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
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 pull request adds dependency injection support to DurableTaskTestHost, enabling orchestrations and activities to resolve services from a DI container. The implementation provides two approaches: ConfigureServices for simple test scenarios and AddInMemoryDurableTask() for integration with existing hosts.
Changes:
- Added
Servicesproperty toDurableTaskTestHostandConfigureServicesoption toDurableTaskTestHostOptions - Introduced
AddInMemoryDurableTask()extension method for integrating with existing service collections - Created comprehensive test suites demonstrating both DI approaches
- Updated README with detailed documentation and examples
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| src/InProcessTestHost/DurableTaskTestHost.cs | Added Services property exposing worker host's service provider and ConfigureServices option |
| src/InProcessTestHost/DurableTaskTestExtensions.cs | New extension methods for AddInMemoryDurableTask() and GetInMemoryOrchestrationService() |
| src/InProcessTestHost/README.md | Comprehensive documentation update with examples for both DI approaches |
| test/InProcessTestHost.Tests/DependencyInjectionTests.cs | New test suite for ConfigureServices approach with multiple DI scenarios |
| test/InProcessTestHost.Tests/WebApplicationFactoryIntegrationTests.cs | New test suite for AddInMemoryDurableTask() integration approach |
| test/InProcessTestHost.Tests/WebApplicationFactoryTestHelpers.cs | Test helper types for integration tests |
| Directory.Packages.props | Added package references (currently unused) |
| /// existing test host so that your orchestrations and activities can resolve services from DI container. | ||
| public static class DurableTaskTestExtensions | ||
| { | ||
| /// <summary> | ||
| /// These extensions allow you to inject the <see cref="InMemoryOrchestrationService"/> into your | ||
| /// existing test host so that your orchestrations and activities can resolve services from DI container. |
Copilot
AI
Jan 13, 2026
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 parameter documentation incorrectly states "from DI container" which is grammatically incorrect. It should be "from the DI container" or "from your DI container".
| /// existing test host so that your orchestrations and activities can resolve services from DI container. | |
| public static class DurableTaskTestExtensions | |
| { | |
| /// <summary> | |
| /// These extensions allow you to inject the <see cref="InMemoryOrchestrationService"/> into your | |
| /// existing test host so that your orchestrations and activities can resolve services from DI container. | |
| /// existing test host so that your orchestrations and activities can resolve services from your DI container. | |
| public static class DurableTaskTestExtensions | |
| { | |
| /// <summary> | |
| /// These extensions allow you to inject the <see cref="InMemoryOrchestrationService"/> into your | |
| /// existing test host so that your orchestrations and activities can resolve services from your DI container. |
| private readonly string address; | ||
| private readonly InMemoryOrchestrationService orchestrationService; | ||
| private readonly ILoggerFactory? loggerFactory; | ||
| private IHost? sidecarHost; |
Copilot
AI
Jan 13, 2026
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.
There's inconsistent naming for the internal class. The class is named "InMemoryGrpcSidecarHost" but the field storing the host instance is called "sidecarHost". Consider renaming the field to "grpcSidecarHost" or renaming the class to "InMemorySidecarHost" for consistency.
| /// </summary> | ||
| public class GreetingActivity : TaskActivity<string, string> | ||
| { | ||
| private readonly IGreetingService _greetingService; |
Copilot
AI
Jan 13, 2026
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 private field naming does not follow the convention used elsewhere in this codebase. According to the coding guidelines, "Use this. for accessing class members", which implies private fields should be named to work with this. prefix. The field should be renamed from _greetingService to greetingService to match the codebase convention seen in other files like DurableTaskTestExtensions.cs (e.g., this.address, this.orchestrationService).
|
|
||
| public GreetingActivity(IGreetingService greetingService) | ||
| { | ||
| _greetingService = greetingService; |
Copilot
AI
Jan 13, 2026
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 private field naming does not follow the convention used elsewhere in this codebase. According to the coding guidelines, "Use this. for accessing class members". The field should be renamed from _greetingService to greetingService and accessed with this.greetingService to match the codebase convention.
| readonly IUserRepository repo; | ||
|
|
||
| public UserService(IUserRepository repo) => this.repo = repo; | ||
|
|
||
| public UserDto? GetUser(int id) | ||
| { | ||
| var u = this.repo.GetById(id); |
Copilot
AI
Jan 13, 2026
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 readonly field naming does not follow the convention used elsewhere in this codebase. The field should be renamed from repo to match a more descriptive name like repository or userRepository, consistent with the interface name IUserRepository.
| readonly IUserRepository repo; | |
| public UserService(IUserRepository repo) => this.repo = repo; | |
| public UserDto? GetUser(int id) | |
| { | |
| var u = this.repo.GetById(id); | |
| readonly IUserRepository userRepository; | |
| public UserService(IUserRepository userRepository) => this.userRepository = userRepository; | |
| public UserDto? GetUser(int id) | |
| { | |
| var u = this.userRepository.GetById(id); |
| /// <summary> | ||
| /// Internal hosted service that runs the gRPC sidecar within the user's host. | ||
| /// </summary> | ||
| internal class InMemoryGrpcSidecarHost : IHostedService, IAsyncDisposable |
Copilot
AI
Jan 13, 2026
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.
According to the coding guidelines, "Ensure that all private classes, that do not serve as base classes, are sealed." The internal class InMemoryGrpcSidecarHost should be marked as sealed since it doesn't serve as a base class.
| readonly IOrderRepository repo; | ||
|
|
||
| public OrderService(IOrderRepository repo) => this.repo = repo; | ||
|
|
||
| public Order? GetOrder(int id) => this.repo.GetById(id); | ||
|
|
||
| public void UpdateStatus(int id, string status) | ||
| { | ||
| var order = this.repo.GetById(id); | ||
| if (order != null) | ||
| { | ||
| order.Status = status; | ||
| this.repo.Update(order); |
Copilot
AI
Jan 13, 2026
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 readonly field naming does not follow the convention used elsewhere in this codebase. The field should be renamed from repo to match a more descriptive name like repository or orderRepository, consistent with the interface name IOrderRepository.
| readonly IOrderRepository repo; | |
| public OrderService(IOrderRepository repo) => this.repo = repo; | |
| public Order? GetOrder(int id) => this.repo.GetById(id); | |
| public void UpdateStatus(int id, string status) | |
| { | |
| var order = this.repo.GetById(id); | |
| if (order != null) | |
| { | |
| order.Status = status; | |
| this.repo.Update(order); | |
| readonly IOrderRepository orderRepository; | |
| public OrderService(IOrderRepository orderRepository) => this.orderRepository = orderRepository; | |
| public Order? GetOrder(int id) => this.orderRepository.GetById(id); | |
| public void UpdateStatus(int id, string status) | |
| { | |
| var order = this.orderRepository.GetById(id); | |
| if (order != null) | |
| { | |
| order.Status = status; | |
| this.orderRepository.Update(order); |
| /// These extensions allow you to inject the <see cref="InMemoryOrchestrationService"/> into your | ||
| /// existing test host so that your orchestrations and activities can resolve services from DI container. |
Copilot
AI
Jan 13, 2026
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 XML documentation comment is duplicated. Lines 25-26 and lines 30-31 contain nearly identical text about injecting InMemoryOrchestrationService. The first comment (lines 25-26) appears to be a class-level comment that was accidentally left in, while lines 30-31 are the method-level documentation. Remove the duplicate text on lines 25-26.
| /// These extensions allow you to inject the <see cref="InMemoryOrchestrationService"/> into your | |
| /// existing test host so that your orchestrations and activities can resolve services from DI container. |
|
|
||
| public class AddActivity : TaskActivity<CalculatorInput, int> | ||
| { | ||
| private readonly ICalculatorService _calculator; |
Copilot
AI
Jan 13, 2026
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 private field naming does not follow the convention used elsewhere in this codebase. The field should be renamed from _calculator to calculator to match the codebase convention of using camelCase without underscore prefix.
|
|
||
| public class CountingActivity : TaskActivity<object?, int> | ||
| { | ||
| private readonly ICounterService _counter; |
Copilot
AI
Jan 13, 2026
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 private field naming does not follow the convention used elsewhere in this codebase. The field should be renamed from _counter to counter to match the codebase convention of using camelCase without underscore prefix.
Adds two approaches for dependency injection in
DurableTaskTestHost:WebApplicationFactory)