Вот как я это реализовал.Я могу поделиться гораздо больше о Github, если этот подход вас интересует.Я использую поток данных довольно часто, поэтому я реализовал множество других классов IDataFlow на основе этого подхода.
Строительный блок
По сути, оборачивая каждое сообщение в класс, называемый Flow<T>
, мы можем реализовать подход Railway Oriented .Поток имеет два состояния: неудача или успех.Успешные потоки Flow<T>
передаются в следующий пользовательский поток данных или подключаются к FailureBlock : ITargetBlock<IFlow>
в случае сбоя.(по существу, ActionBlock<IFlow>
, который имеет дело с исключениями, журналами и т. д.
Мой базовый класс Flow выглядит следующим образом:
public class Flow<T> : IFlow
{
public T Value { get; private set; }
public Exception Exception { get; private set; }
public bool Success => Exception is null;
public bool Failure => !Success;
public void Fail(Exception exception) => Exception = exception;
public Flow(T value) => Data = value;
public Flow(Exception exception) => Fail(exception);
public static Flow<T> FromValue<T>(T data) => new Flow<T>(data);
}
public interface IFlow
{
bool Success { get; }
bool Failure { get; }
Exception Exception { get; }
void Fail(Exception exception);
}
Результирующий пользовательский поток данных IData
Следующая часть выглядит страшно, но не надо. По сути, это оболочка TransformBlock с двумя дополнительными функциями:
введите код здесь 1. каждый пользовательский FlowBlock<T1,T2>
упаковывает методы в try { } catch { }
метод
LinkTo
связывает успешные потоки со следующим блоком и сбои с
FailureBlock
public class FlowBlock<TInput, TOutput>: IPropagatorBlock<Flow<TInput>, Flow<TOutput>>
{
protected override ITargetBlock<Flow<TInput>> Target => TransformBlock;
protected override ISourceBlock<Flow<TOutput>> Source => TransformBlock;
private TransformBlock<Flow<TInput>, Flow<TOutput>> TransformBlock { get; }
private FailureBlock FailureBlock { get; }
public FlowBlock(
Func<TInput, Task<TOutput>> transform,
ExecutionDataflowBlockOptions dataflowBlockOptions,
FailureBlock failureBlock)
{
TransformBlock = new TransformBlock<Flow<TInput>, Flow<TOutput>>(
async inFlow =>
{
try
{
return new Flow<TOutput>(await transform(inFlow.Data));
}
catch (Exception exception)
{
return new Flow<TOutput>(exception);
}
},
dataflowBlockOptions);
}
public override IDisposable LinkTo(
ITargetBlock<Flow<TOutput>> target,
DataflowLinkOptions linkOptions)
=> new Disposable(
Source.LinkTo(target, linkOptions, flow => flow.Success),
Source.LinkTo(OutputBlock, linkOptions, flow => flow.Failure));
}
Дайте мне знать в комментариях, если вы заинтересованы, и я с удовольствием откроюрепозиторий Github с гораздо большим количеством деталей.