Skip to content

Conversation

@nytian
Copy link
Contributor

@nytian nytian commented Jan 13, 2026

Adds two approaches for dependency injection in DurableTaskTestHost:

  1. ConfigureServices - Register services directly in test host options
  2. AddInMemoryDurableTask() - Integrate with existing hosts (e.g., WebApplicationFactory)

Copilot AI review requested due to automatic review settings January 13, 2026 05:27
Copy link
Contributor

Copilot AI left a 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 Services property to DurableTaskTestHost and ConfigureServices option to DurableTaskTestHostOptions
  • 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)

Comment on lines +26 to +31
/// 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.
Copy link

Copilot AI Jan 13, 2026

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".

Suggested change
/// 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.

Copilot uses AI. Check for mistakes.
private readonly string address;
private readonly InMemoryOrchestrationService orchestrationService;
private readonly ILoggerFactory? loggerFactory;
private IHost? sidecarHost;
Copy link

Copilot AI Jan 13, 2026

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.

Copilot uses AI. Check for mistakes.
/// </summary>
public class GreetingActivity : TaskActivity<string, string>
{
private readonly IGreetingService _greetingService;
Copy link

Copilot AI Jan 13, 2026

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).

Copilot generated this review using guidance from repository custom instructions.

public GreetingActivity(IGreetingService greetingService)
{
_greetingService = greetingService;
Copy link

Copilot AI Jan 13, 2026

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.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +82 to +88
readonly IUserRepository repo;

public UserService(IUserRepository repo) => this.repo = repo;

public UserDto? GetUser(int id)
{
var u = this.repo.GetById(id);
Copy link

Copilot AI Jan 13, 2026

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
/// <summary>
/// Internal hosted service that runs the gRPC sidecar within the user's host.
/// </summary>
internal class InMemoryGrpcSidecarHost : IHostedService, IAsyncDisposable
Copy link

Copilot AI Jan 13, 2026

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.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +101 to +113
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);
Copy link

Copilot AI Jan 13, 2026

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +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.
Copy link

Copilot AI Jan 13, 2026

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.

Suggested change
/// 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 uses AI. Check for mistakes.

public class AddActivity : TaskActivity<CalculatorInput, int>
{
private readonly ICalculatorService _calculator;
Copy link

Copilot AI Jan 13, 2026

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.

Copilot generated this review using guidance from repository custom instructions.

public class CountingActivity : TaskActivity<object?, int>
{
private readonly ICounterService _counter;
Copy link

Copilot AI Jan 13, 2026

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.

Copilot generated this review using guidance from repository custom instructions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants