microsoft / microsoft/agent-framework
.NET: Excessive boilerplate for executors compared to Python - poor discoverability of simpler patterns
- Dominant language
- Python
- Stars
- 13.6k
- Forks
- 2.3k
- Avg merge
- 2d 45m
- Merged PRs (30d)
- 358
Description
## Summary
Creating executors in .NET requires significantly more boilerplate code compared to the Python SDK, creating a poor developer experience and high barrier to entry. While simpler patterns exist (e.g., .AsExecutor() for functions), they are poorly documented and not shown in official samples.
Python SDK (3 lines):
```
@executor(id="reverse_text_executor")
async def reverse_text(text: str, ctx: WorkflowContext[Never, str]) -> None:
result = text[::-1]
await ctx.yield_output(result)
```
.NET SDK - Official Pattern (10+ lines):
```
internal sealed class ReverseExecutor : ReflectingExecutor, IMessageHandler
{
public ReverseExecutor() : base("ReverseExecutor") { }
public ValueTask HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
{
var result = string.Concat(message.Reverse());
return ValueTask.FromResult(result);
}
}
```
.NET SDK - Undocumented Simpler Pattern (3 lines):
```
Func> reverseFunc =
async (text, ctx, ct) => string.Concat(text.Reverse());
var executor = reverseFunc.AsExecutor("ReverseExecutor");
```
Problem: The simpler pattern exists but is hidden - all documentation and samples use the verbose class-based approach.
---
## Current Issues
1. Excessive Boilerplate Required
Class-based pattern requires:
• Creating a class (sealed for performance)
• Inheriting from ReflectingExecutor with recursive generic
• Implementing IMessageHandler interface
• Writing a constructor that calls base with ID
• Implementing HandleAsync with all parameters
• Understanding ValueTask, CancellationToken, etc.
For a simple function, this is 10x more code than Python
2. Poor Discoverability of Alternatives
The .AsExecutor() extension method exists but:
• ❌ Not mentioned in official documentation
• ❌ Not shown in any samples
• ❌ Not discoverable through IntelliSense on functions
• ❌ No examples in getting started guide
Result: Developers assume the verbose pattern is the only way.
3. Documentation Mismatch
Python tutorial (https://learn.microsoft.com/en-us/agent-framework/tutorials/workflows/simple-sequential-workflow?pivots=programming-language-python):
• Shows clean, simple @executor decorator
• 3 lines of code
• Focus on logic, not framework ceremony
.NET tutorial (https://learn.microsoft.com/en-us/agent-framework/tutorials/workflows/simple-sequential-workflow?pivots=programming-language-csharp):
• Shows verbose class-based pattern
• 10+ lines of boilerplate
• Framework ceremony obscures business logic
Creates perception that C# SDK is harder to use
4. High Barrier to Entry
New developers face:
• Must learn ReflectingExecutor, IMessageHandler, ValueTask
• Must understand generic type parameters
• Must know C# async patterns
• Must create multiple files/classes for simple logic
Python developers switching to C# face significant friction
I myself I am tempted to switch to Python, basically...
---
## Proposed Solutions
Solution 1: Promote Function-Based Pattern in Documentation
Update documentation and samples to show simpler pattern first:
```
// ✅ Simple function-based executor (recommended for simple logic)
var reverseExecutor = ((string text, IWorkflowContext ctx, CancellationToken ct)
=> ValueTask.FromResult(string.Concat(text.Reverse())))
.AsExecutor("ReverseExecutor");
// Class-based executor (use for complex logic with state)
internal sealed class ComplexExecutor : ReflectingExecutor, IMessageHandler
{
// Use when you need: state, dependency injection, complex logic
}
```
Changes needed:
• Update tutorial to show function-based pattern first
• Add comparison section: "Simple vs Complex Executors"
• Update all samples to use function-based for simple cases
• Add XML docs to .AsExecutor() extension method
• Create "Best Practices" guide
---
Solution 2: Decorator based by using Source Generators - very similar to Python conceptually
```
[Executor("reverse_text_executor")]
public static string ReverseText(string text)
{
return string.Concat(text.Reverse());
}
// Source generator creates boilerplate class automatically
```
Pros: Best of both worlds - simple syntax, full power
Cons: Requires source generator infrastructure, C# 9+
## Impact
• Priority: High (affects all developers, especially new users)
• Severity: Major developer experience issue
• Audience:
• New developers learning the framework
• Python developers migrating to C#
• Developers building simple workflows
• Workaround Available: Yes (.AsExecutor() exists but undocumented)
• User Experience: Poor - excessive ceremony discourages adoption
• Perception: Makes C# SDK appear more complex than it needs to be
Contributor guide
Assessment
This issue has not been assessed yet.