Jef*_*eff 9 c# asynchronous async-await
我正在尝试设计一个公开添加异步处理问题的能力的类。在同步编程中,这可能看起来像
public class ProcessingArgs : EventArgs
{
public int Result { get; set; }
}
public class Processor
{
public event EventHandler<ProcessingArgs> Processing { get; }
public int Process()
{
var args = new ProcessingArgs();
Processing?.Invoke(args);
return args.Result;
}
}
var processor = new Processor();
processor.Processing += args => args.Result = 10;
processor.Processing += args => args.Result+=1;
var result = processor.Process();
Run Code Online (Sandbox Code Playgroud)
在异步世界中,每个关注点都可能需要返回一个任务,这并不是那么简单。我已经看到了很多方法,但我很好奇人们是否找到了任何最佳实践。一种简单的可能性是
public class Processor
{
public IList<Func<ProcessingArgs, Task>> Processing { get; } =new List<Func<ProcessingArgs, Task>>();
public async Task<int> ProcessAsync()
{
var args = new ProcessingArgs();
foreach(var func in Processing)
{
await func(args);
}
return args.Result
}
}
Run Code Online (Sandbox Code Playgroud)
人们为此采用了一些“标准”吗?我在流行的 API 中观察到的似乎没有一致的方法。
以下委托将用于处理异步实现问题
public delegate Task PipelineStep<TContext>(TContext context);
Run Code Online (Sandbox Code Playgroud)
从评论中可以看出
一个具体示例是添加完成“事务”(LOB 功能)所需的多个步骤/任务
下面的类允许构建一个委托,以类似于 .net core 中间件的流畅方式处理这些步骤
public class PipelineBuilder<TContext> {
private readonly Stack<Func<PipelineStep<TContext>, PipelineStep<TContext>>> steps =
new Stack<Func<PipelineStep<TContext>, PipelineStep<TContext>>>();
public PipelineBuilder<TContext> AddStep(Func<PipelineStep<TContext>, PipelineStep<TContext>> step) {
steps.Push(step);
return this;
}
public PipelineStep<TContext> Build() {
var next = new PipelineStep<TContext>(context => Task.CompletedTask);
while (steps.Any()) {
var step = steps.Pop();
next = step(next);
}
return next;
}
}
Run Code Online (Sandbox Code Playgroud)
以下扩展允许使用包装器进行更简单的内联设置
public static class PipelineBuilderAddStepExtensions {
public static PipelineBuilder<TContext> AddStep<TContext>
(this PipelineBuilder<TContext> builder,
Func<TContext, PipelineStep<TContext>, Task> middleware) {
return builder.AddStep(next => {
return context => {
return middleware(context, next);
};
});
}
public static PipelineBuilder<TContext> AddStep<TContext>
(this PipelineBuilder<TContext> builder, Func<TContext, Task> step) {
return builder.AddStep(async (context, next) => {
await step(context);
await next(context);
});
}
public static PipelineBuilder<TContext> AddStep<TContext>
(this PipelineBuilder<TContext> builder, Action<TContext> step) {
return builder.AddStep((context, next) => {
step(context);
return next(context);
});
}
}
Run Code Online (Sandbox Code Playgroud)
它可以根据需要进一步扩展以获得额外的包装器。
以下测试演示了委托实际使用的示例用例
[TestClass]
public class ProcessBuilderTests {
[TestMethod]
public async Task Should_Process_Steps_In_Sequence() {
//Arrange
var expected = 11;
var builder = new ProcessBuilder()
.AddStep(context => context.Result = 10)
.AddStep(async (context, next) => {
//do something before
//pass context down stream
await next(context);
//do something after;
})
.AddStep(context => { context.Result += 1; return Task.CompletedTask; });
var process = builder.Build();
var args = new ProcessingArgs();
//Act
await process.Invoke(args);
//Assert
args.Result.Should().Be(expected);
}
public class ProcessBuilder : PipelineBuilder<ProcessingArgs> {
}
public class ProcessingArgs : EventArgs {
public int Result { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)