实现Rx处理程序的最佳实践是什么?

时间:2014-01-14 14:10:46

标签: c# .net system.reactive reactive-programming

我有这堂课来解释我的问题:

public class DataObserver: IDisposable
{
    private readonly List<IDisposable> _subscriptions = new List<IDisposable>();
    private readonly SomeBusinessLogicServer _server;

    public DataObserver(SomeBusinessLogicServer server, IObservable<SomeData> data)
    {
        _server = server;
        _subscriptions.Add(data.Subscribe(TryHandle));
    }

    private void TryHandle(SomeData data)
    {
        try
        {
            _server.MakeApiCallAsync(data).Wait();
        }
        catch (Exception)
        {
            // Handle exceptions somehow!
        }
    }

    public void Dispose()
    {
        _subscriptions.ForEach(s => s.Dispose());
        _subscriptions.Clear();
    }

}

A)如何避免在TryHandle()函数内部阻塞?

B)你如何发布在该函数中捕获的异常以便正确处理它们?

2 个答案:

答案 0 :(得分:3)

在编写自己的Rx运算符时,Rx设计指南提供了许多有用的建议:

http://go.microsoft.com/fwlink/?LinkID=205219

我确信我会因为链接到外部文章而受到抨击,但是这个链接已经存在了好几年而且在SO上重新发布它太大了。

答案 1 :(得分:2)

首先,请查看CompositeDisposable,而不是自己重新实施。

除此之外,您的问题有很多答案。我发现在使用Rx时我所拥有的最好的洞察力是意识到你想要订阅的大多数情况实际上只是你正在构建的可观察链中的更多链,你真的不想订阅而是想申请另一个转变为传入的观察者。让一些代码进一步“在系统的边缘”,并且有更多关于如何处理错误的知识做实际的订阅

在您提供的示例中:

A)不要仅仅将IObservable<SomeData>转换为IObservable<Task>(实际上更好地表达为IObservable<IObservable<Unit>>)来阻止。 B)通过仅使用错误结束observable来发布异常,或者,如果您不希望异常结束observable,则公开IObservable<Exception>

以下是我重新编写示例的方法,假设您不希望流在出错时结束,而是在报告错误后继续运行:

public static class DataObserver
{
    public static IObservable<Exception> ApplyLogic(this IObservable<SomeData> source, SomeBusinessLogicServer server)
    {
        return source
            .Select(data =>
                {
                    // execute the async method as an observable<Unit>
                    // ignore its results, but capture its error (if any) and yield it.
                    return Observable
                        .FromAsync(() => server.MakeApiCallAsync(data))
                        .IgnoreElements()
                        .Select(_ => (Exception)null) // to cast from IObservable<Unit> to IObservable<Exception>
                        .Catch((Exception e) => Observable.Return(e));
                })
            // runs the Api calls sequentially (so they will not run concurrently)
            // If you prefer to let the calls run in parallel, then use
            // .Merge() instead of .Concat()
            .Concat() ;
    }
}


// Usage (in Main() perhaps)
IObservable<SomeData> dataStream = ...;
var subscription = dataStream.ApplyLogic(server).Subscribe(error =>
{
    Console.WriteLine("An error occurred processing a dataItem: {0}", error);
}, fatalError =>
{
    Console.WriteLine("A fatal error occurred retrieving data from the dataStream: {0}", fatalError);
});