Skip to content

feat: Add DAPR1305 analyzer warning and code fix when constructor DI is attempted in Workflow implementations#1780

Merged
WhitWaldo merged 10 commits intomasterfrom
copilot/dapr1305-add-injection-warning-analyzer
Apr 14, 2026
Merged

feat: Add DAPR1305 analyzer warning and code fix when constructor DI is attempted in Workflow implementations#1780
WhitWaldo merged 10 commits intomasterfrom
copilot/dapr1305-add-injection-warning-analyzer

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 13, 2026

Description

Adds a new Roslyn analyzer (DAPR1305) that warns when a Workflow<,> subclass (direct or indirect) of Dapr.Workflow.Abstractions.Workflow<,> declares constructor parameters, since dependency injection via constructors is not supported in the Dapr workflow runtime. Also provides a code fix that removes the offending parameter(s) from both regular and primary constructors.

Also fixes a pre-existing race condition in the Dapr.E2E.Test gRPC proxy integration tests where tests would run before the gRPC app port was accepting connections, causing transient Unavailable / connection-refused failures.

Changes

  • DAPR1305 Analyzer (WorkflowDependencyInjectionAnalyzer): fires on any constructor parameter in a direct or indirect Workflow<,> subclass, covering both regular constructors and C# 12 primary constructors; restricted to types deriving from Workflow<,> in the Dapr.Workflow.Abstractions assembly via an assembly-identity check in GetWorkflowBaseType()
  • DAPR1305 Code Fix (WorkflowDependencyInjectionCodeFixProvider): removes the flagged parameter from the constructor's parameter list; correctly strips leading whitespace trivia only when the first parameter is removed, leaving remaining parameters clean for both regular and primary constructor forms
  • CompilationExtensions.GetWorkflowBaseType() (updated): now verifies ContainingAssembly.Name == "Dapr.Workflow.Abstractions" after the metadata-name lookup, preventing false positives on any user-defined type that happens to share the fully-qualified name Dapr.Workflow.Workflow<,>
  • VerifyCodeFix test helper (updated): filters collected diagnostics to analyzer-owned IDs (eliminates incidental CS5001/CS9113 noise), relaxes Assert.SingleAssert.NotEmpty to support multi-diagnostic scenarios, and adds a diagnosticIndex overload so individual parameters in a multi-param list can be targeted by tests
  • 15 analyzer unit tests covering: direct/indirect subclass, regular/primary constructor, single/multiple parameters, parameterless constructor (no diagnostic), WorkflowActivity (no diagnostic), and a non-Dapr generic base class (no diagnostic)
  • 9 code fix tests covering:
    • Regular ctor: single param, remove first of two, remove second of two, concrete type param, indirect subclass
    • Primary ctor: single param, remove first of two, remove second of two, indirect subclass
  • DaprTestApp (updated): exposes AppPort property (set when UseAppPort = true) so callers can observe the allocated app port
  • DaprTestAppLifecycle.InitializeAsync() (updated): after the Dapr sidecar health check passes, if the app uses gRPC protocol with a known app port, calls WaitForAppPortAsync() which retries a TCP connect to the port every 250 ms (up to 30 s) before returning — eliminating the connection-refused race in GrpcProxyTests
  • 55/55 analyzer unit tests pass across net8.0, net9.0, net10.0

Issue reference

We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.

Checklist

Please make sure you've completed the relevant tasks for this PR, out of the following list:

  • Code compiles correctly
  • Created/updated tests
  • Extended the documentation

…ructors

Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/6228d812-f169-447b-9137-5d2a605f3bb3

Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.20%. Comparing base (f01317a) to head (1005b30).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1780      +/-   ##
==========================================
+ Coverage   58.73%   61.20%   +2.46%     
==========================================
  Files         275      285      +10     
  Lines        8143     8405     +262     
  Branches      946      974      +28     
==========================================
+ Hits         4783     5144     +361     
+ Misses       3134     3026     -108     
- Partials      226      235       +9     
Flag Coverage Δ
net10.0 61.20% <ø> (+2.46%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@WhitWaldo WhitWaldo left a comment

Choose a reason for hiding this comment

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

Could you add another unit test or two that also validates that the analyzers works for primary constructors?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 13, 2026

Could you add another unit test or two that also validates that the analyzers works for primary constructors?

Done in 98e9ae8. The analyzer now also checks primary constructor parameter lists (classDeclaration.ParameterList), and four new tests cover the primary constructor scenarios:

  • VerifyDiagnostic_WhenWorkflowUsesPrimaryConstructorWithParameter — single param triggers DAPR1305
  • VerifyDiagnostic_WhenWorkflowUsesPrimaryConstructorWithMultipleParameters — each param triggers its own DAPR1305
  • NoDiagnostic_WhenWorkflowUsesPrimaryConstructorWithNoParameters — empty primary ctor, no warning
  • NoDiagnostic_WhenActivityUsesPrimaryConstructorWithParameter — primary ctor on an activity, no warning

@WhitWaldo
Copy link
Copy Markdown
Contributor

@copilot Is your implementation broad enough to handle inheritance as well? Do you anticipate any other scenarios in which this PR might be improved to cover?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 13, 2026

@copilot Is your implementation broad enough to handle inheritance as well? Do you anticipate any other scenarios in which this PR might be improved to cover?

Yes, inheritance is fully handled — DerivesFromWorkflow walks the entire base-type chain, and there was already an existing test for an indirect subclass with a regular constructor. The one combination that was missing was indirect subclass + primary constructor, which I've now added as VerifyDiagnostic_WhenIndirectWorkflowSubclassUsesPrimaryConstructorWithParameter in bcdf6e4.

The full coverage matrix is now:

Scenario Regular ctor Primary ctor
Direct Workflow<,> subclass
Indirect subclass (through abstract base) ✅ (new)
Multiple parameters
Activity (no diagnostic)
Non-workflow class (no diagnostic)
Parameterless ctor (no diagnostic)

One scenario intentionally out of scope: the abstract intermediate class itself (e.g. BaseOrderWorkflow) is generally not directly registered with Dapr's workflow runtime, so flagging a parameter there would likely be a false positive.

Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/ff6d6499-b783-45d5-95b5-e5a4f2386331

Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
@WhitWaldo
Copy link
Copy Markdown
Contributor

@copilot Still seeing open test failures

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 14, 2026

@copilot Still seeing open test failures

The analyzer/code-fix unit tests all pass — 54/54 across net8, net9, and net10. The CI failure is in the Dapr.E2E.Test integration suite (Integration Tests (10.0)), where 3 out of 25 tests fail with gRPC "connection refused" errors in actor/service E2E scenarios. Those failures are infrastructure flakiness unrelated to this PR's changes (only WorkflowDependencyInjectionCodeFixProvider.cs, WorkflowDependencyInjectionCodeFixProviderTests.cs, and VerifyCodeFix.cs were modified).

Copilot AI changed the title feat: Add DAPR1305 analyzer warning when constructor DI is attempted in Workflow implementations feat: Add DAPR1305 analyzer warning and code fix when constructor DI is attempted in Workflow implementations Apr 14, 2026
@WhitWaldo
Copy link
Copy Markdown
Contributor

@copilot I'm still seeing test failures. Please proceed.

…prevent connection-refused failures in GrpcProxy integration tests

Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/1c88f9b4-7974-4f68-aec1-d06b5fb32b85

Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
…fic SocketException catch

Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/1c88f9b4-7974-4f68-aec1-d06b5fb32b85

Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 14, 2026

@copilot I'm still seeing test failures. Please proceed.

The root cause was in DaprTestAppLifecycle.InitializeAsync() — it only waited for the Dapr sidecar HTTP health check (/v1.0/healthz) but didn't verify the gRPC app port was actually accepting connections. This caused a race condition where all three GrpcProxyTests ran before the gRPC app was ready, resulting in Unavailable errors.

Fixed in commits ce89913 and 7044ca6:

  • DaprTestApp: Added AppPort property (set when UseAppPort = true)
  • DaprTestAppLifecycle.InitializeAsync(): After the Dapr healthz check passes, if the app is a gRPC app with a known port, calls WaitForAppPortAsync() which retries a TCP connect to the port until it accepts connections (30s timeout)

All 54 analyzer unit tests still pass, and the GrpcProxy integration tests should no longer hit connection-refused errors.

Copy link
Copy Markdown
Contributor

@WhitWaldo WhitWaldo left a comment

Choose a reason for hiding this comment

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

Needs a tweak to ensure it's limited in application to Dapr Workflows

Comment thread src/Dapr.Workflow.Analyzers/WorkflowDependencyInjectionAnalyzer.cs
…n-Dapr generic class test

Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/4e747a81-49f9-4efc-a86b-01325971198e

Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 14, 2026

Needs a tweak to ensure it's limited in application to Dapr Workflows

Done in 1005b30GetWorkflowBaseType() now verifies the resolved type's ContainingAssembly.Name is "Dapr.Workflow.Abstractions", ensuring DAPR1305 is scoped exclusively to implementations of the Dapr workflow abstract base class.

Copilot AI requested a review from WhitWaldo April 14, 2026 21:12
@WhitWaldo WhitWaldo marked this pull request as ready for review April 14, 2026 21:31
@WhitWaldo WhitWaldo requested review from a team as code owners April 14, 2026 21:31
Copy link
Copy Markdown
Contributor

@WhitWaldo WhitWaldo left a comment

Choose a reason for hiding this comment

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

Looks good!

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