-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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<ReverseExecutor>, IMessageHandler<string, string>
{
public ReverseExecutor() : base("ReverseExecutor") { }
public ValueTask<string> 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<string, IWorkflowContext, CancellationToken, ValueTask<string>> 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
-
Excessive Boilerplate Required
Class-based pattern requires:
• Creating a class (sealed for performance)
• Inheriting from ReflectingExecutor with recursive generic
• Implementing IMessageHandler<TIn, TOut> 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 -
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. -
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
- 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<ComplexExecutor>, IMessageHandler<string, string>
{
// 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
Metadata
Metadata
Assignees
Labels
Type
Projects
Status