C# 如何获取异常的上下文
我正在使用由Stephen Cleary()设计的C# 如何获取异常的上下文,c#,task-parallel-library,pipeline,tpl-dataflow,C#,Task Parallel Library,Pipeline,Tpl Dataflow,我正在使用由Stephen Cleary()设计的TaskParallelLibrary DataFlow与Try库相结合来实现所谓的“铁路编程”,这样我就可以通过管道传递异常数据。我想知道,在ActionBlock中,是否有可能获得导致异常的内容或(在我的情况下)确切的项目上下文? 以下是小示例代码: public async Task TestRailroadException(List<int> constructIds) { var downloadBlock = n
TaskParallelLibrary DataFlow
与Try
库相结合来实现所谓的“铁路编程”,这样我就可以通过管道传递异常
数据。我想知道,在ActionBlock
中,是否有可能获得导致异常的内容或(在我的情况下)确切的项目上下文?
以下是小示例代码:
public async Task TestRailroadException(List<int> constructIds)
{
var downloadBlock = new TransformBlock<int, Try<int>>(
construct => Try.Create(() =>
{
//ThisMethodMyThrowException();
return 1;
}));
var processBlock = new TransformBlock<Try<int>, Try<int>>(
construct => construct.Map(value =>
{
//ThisMethodMyAlsoThrowException();
return 1;
}));
var resultsBlock = new ActionBlock<Try<int>>(construct =>
{
if (construct.IsException)
{
var type = construct.Exception.GetType();
//Here it would be nice to know which item(id) was faulted.
}
});
downloadBlock.LinkTo(processBlock, new DataflowLinkOptions
{ PropagateCompletion = true });
processBlock.LinkTo(resultsBlock, new DataflowLinkOptions
{ PropagateCompletion = true });
foreach (var constructId in constructIds)
{
await downloadBlock.SendAsync(constructId);
}
downloadBlock.Complete();
await resultsBlock.Completion;
}
公共异步任务TestRailroadException(列出构造函数ID)
{
var downloadBlock=新TransformBlock(
construct=>Try.Create(()=>
{
//ThisMethodMyRowException();
返回1;
}));
var processBlock=新转换块(
construct=>construct.Map(值=>
{
//ThisMethodMyAlsoThrowException();
返回1;
}));
var resultsBlock=新操作块(构造=>
{
if(construct.IsException)
{
var type=construct.Exception.GetType();
//在这里,最好知道哪个项目(id)出现故障。
}
});
LinkTo(processBlock,新的DataflowLinkOptions
{PropagateCompletion=true});
LinkTo(resultsBlock,新的DataflowLinkOptions
{PropagateCompletion=true});
foreach(constructIds中的var constructId)
{
等待downloadBlock.SendAsync(constructId);
}
downloadBlock.Complete();
等待结果块。完成;
}
您可以使用ValueTuple
结构作为管道的消息,但创建同样保存id的Try
类的自定义包装可能会稍微方便一些。由于此包装将有两个类型参数,因此允许将其命名为Try
:
public readonly struct Try<TId, TResult>
{
public static Try<TId, TResult> Create(TId id, Func<TResult> func)
=> new Try<TId, TResult>(id, Try.Create(func));
public static async Task<Try<TId, TResult>> Create(TId id,
Func<Task<TResult>> func)
=> new Try<TId, TResult>(id, await Try.Create(func).ConfigureAwait(false));
public readonly TId Id { get; }
public readonly Try<TResult> Result { get; }
private Try(TId id, Try<TResult> result) { Id = id; Result = result; }
public Try<TId, TNewResult> Map<TNewResult>(Func<TResult, TNewResult> func)
=> new Try<TId, TNewResult>(Id, Result.Map(func));
public async Task<Try<TId, TNewResult>> Map<TNewResult>(
Func<TResult, Task<TNewResult>> func)
=> new Try<TId, TNewResult>(Id, await Result.Map(func).ConfigureAwait(false));
}
公共只读结构Try
{
公共静态尝试创建(TId id,Func Func)
=>新建Try(id,Try.Create(func));
公共静态异步任务创建(TId id,
Func Func)
=>newtry(id,wait Try.Create(func.ConfigureAwait(false));
公共只读TId Id{get;}
公共只读重试结果{get;}
私有Try(TId-id,Try-result){id=id;result=result;}
公共尝试映射(Func Func)
=>新建Try(Id,Result.Map(func));
公共异步任务映射(
Func Func)
=>新建Try(Id,wait Result.Map(func).configurewait(false));
}
然后您可以这样使用它:
var downloadBlock = new TransformBlock<int, Try<int, int>>(
construct => Try<int, int>.Create(construct, async () =>
{
await SometimesThrowsAsync();
return 1;
}));
var processBlock = new TransformBlock<Try<int, int>, Try<int, int>>(
construct => construct.Map(async value =>
{
await SometimesThrowsAsync();
return 1;
}));
var resultsBlock = new ActionBlock<Try<int, int>>(construct =>
{
if (construct.Result.IsException)
{
var type = construct.Result.Exception.GetType();
//Log that the {construct.Id} has failed.
}
});
var downloadBlock=new TransformBlock(
construct=>Try.Create(construct,async()=>
{
通过异步()等待一段时间;
返回1;
}));
var processBlock=新转换块(
construct=>construct.Map(异步值=>
{
通过异步()等待一段时间;
返回1;
}));
var resultsBlock=新操作块(构造=>
{
if(construct.Result.IsException)
{
var type=construct.Result.Exception.GetType();
//记录{construct.Id}已失败。
}
});
看看这个:。TL;DR类不能用于随每条消息传递环境上下文数据。因此,除了使用元组、自定义类或结构作为消息在块之间手动传递上下文数据之外,别无选择。@Theodor。谢谢你的回答。您认为在每个块中捕获错误,添加带有错误项id的exception.Data
,然后重新抛出它们以在resultsBlock
中再次捕获它们是一个好主意吗?这样我就可以知道是谁导致了异常
,而不必在每个块中重复所有捕获
逻辑。这不是一个很好的解决方案,但它完成了任务。是的,没关系。但是使用斯蒂芬·克利里的图书馆可能更干净。它在技术上也很优越,除非您愿意了解该类,并使用它而不是裸异常来存储异常。:-)var downloadBlock=newtransformblock
怎么样?结果是一个包含两个成员的ValueTuple
,即原始项及其结果,结果包装在Try
中。然后继续将原始项从一个块传递到另一个块,使用元组作为TInput
和TOutput
@Theodor Ah!但是当然!如此简单,却又如此辉煌!非常感谢。如果你把这个作为一个答案和一个小样本一起发布,我绝对会接受!非常好的包装!然而,如果ThisMethodMyRowException
是应该等待的异步方法,那么我正在努力实现它。我就是不能让它工作。你能分享一些技巧吗?@niks我用异步友好的方法更新了Try
。现在应该可以与异步TPL数据流块集成了。再次感谢您!我花了一些时间来理解它,TPL
和async
这些东西不是我的强项..我自己无法想出这样的解决方案!再一次,我对这个网站的工作方式和来自世界各地的人们愿意投入时间帮助他人感到惊讶:)埃法斯特@我的朋友。async/await太棒了!