C# 反应式扩展清理

C# 反应式扩展清理,c#,linq,httpwebrequest,system.reactive,C#,Linq,Httpwebrequest,System.reactive,如果您使用rx进行长链呼叫,例如: var responses = collectionOfHttpRequests.ToObservable() .FromAsyncPattern(req.BeginGetResponse, req.EndGetResponse) .Select(res => res.GetResponseBodyString()) // Extension method to get the body of the request .Subscribe(); 然后在

如果您使用rx进行长链呼叫,例如:

var responses = collectionOfHttpRequests.ToObservable()
.FromAsyncPattern(req.BeginGetResponse, req.EndGetResponse)
.Select(res => res.GetResponseBodyString()) // Extension method to get the body of the request
.Subscribe();
然后在操作完成之前调用dispose,http请求是否会被取消、关闭并正确地处理,或者我是否必须从方法链中选择httprequests并单独处理它们


我有一个功能,一个可以同时发生多个http请求,我需要能够取消(而不是忽略)其中的一些/全部以节省网络流量。

当序列完成、错误或订阅被处理时,Rx运营商链将自行清理。但是,每个操作员只清理他们希望清理的内容。例如,
FromEvent
将取消订阅该事件

在您的情况下,不支持取消,因此Rx不支持取消。但是,您可以使用
Finally
调用


我不能承认Richard Szalay的解决方案是可以接受的。如果启动100个请求,第二个请求失败,出现服务器不可用错误(例如),其余98个请求将被中止。第二个问题是UI将如何对这种可观察的行为做出反应?太悲伤了

记住第4.3章,我希望通过observable.Using()操作符来表示WebRequest observable。但是WebRequest不是一次性的!所以我定义了DisposableWebRequest:

public class DisposableWebRequest : WebRequest, IDisposable
{
    private static int _Counter = 0;

    private readonly WebRequest _request;
    private readonly int _index;

    private volatile bool _disposed = false;

    public DisposableWebRequest (string url)
    {
        this._request = WebRequest.Create(url);
        this._index = ++DisposableWebRequest._Counter;
    }

    public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state)
    {
        return this._request.BeginGetResponse(callback, state);
    }

    public override WebResponse EndGetResponse(IAsyncResult asyncResult)
    {
        Trace.WriteLine(string.Format("EndGetResponse index {0} in thread {1}", this._index, Thread.CurrentThread.ManagedThreadId));
        Trace.Flush();
        if (!this._disposed)
        {
            return this._request.EndGetResponse(asyncResult);
        }
        else
        {
            return null;
        }
    }

    public override WebResponse GetResponse()
    {
        return this._request.GetResponse();
    }

    public override void Abort()
    {
        this._request.Abort();
    }

    public void Dispose()
    {
        if(!this._disposed)
        {
            this._disposed = true;

            Trace.WriteLine(string.Format("Disposed index {0} in thread {1}", this._index, Thread.CurrentThread.ManagedThreadId ));
            Trace.Flush();
            this.Abort();
        }
    }
}
然后我创建WPF窗口并在其上放置两个按钮(开始和停止)

因此,让我们创建适当的请求可观察集合。 首先,定义URL的可观察创建函数:

        Func<IObservable<string>> createUrlObservable = () =>
            Observable
                .Return("http://yahoo.com")
                .Repeat(100)
                .OnStartup(() =>
                {
                    this._failed = 0;
                    this._successed = 0;
                });
您可能会对函数“HandleError()”感到疑惑。如果在EndGetResponse()调用中发生异常(我关闭了网络连接以复制异常),并且在request observable中未捕获到异常-它将使startMouseDown observable崩溃。HandleError以静默方式捕获异常,执行提供的操作,而不是为下一个观察者调用OnCompleted的OnError:

public static class ObservableExtensions
{
    public static IObservable<TSource> HandleError<TSource>(this IObservable<TSource> source, Action<Exception> errorHandler)
    {
        return Observable.CreateWithDisposable<TSource>(observer =>
            {
                return source.Subscribe(observer.OnNext, 
                    e => 
                    { 
                        errorHandler(e);
                        //observer.OnError(e);
                        observer.OnCompleted();
                    },
                    observer.OnCompleted);
            });
    }
}

您的代码似乎不正确:在
IObservable
上没有可调用的扩展方法
fromsyncPattern
。这种方法只是静态的。不是扩展。如果您花时间编写真正的代码,那么问题和解决方案都会变得显而易见。@Fryodor-虽然您对AsyncPattern中的
是绝对正确的,但我不认为使用正确的方法签名可以让OP深入了解如何取消异步请求。
        Func<string, IObservable<WebResponse>> createRequestObservable = 
            url => 
            Observable.Using(
                () => new DisposableWebRequest("http://yahoo.com"),
                r =>
                {
                    Trace.WriteLine("Queued " + url);
                    Trace.Flush();
                    return Observable
                        .FromAsyncPattern<WebResponse>(r.BeginGetResponse, r.EndGetResponse)();
                });
        var startMouseDown = Observable.FromEvent<RoutedEventArgs>(this.StartButton, "Click");
        var stopMouseDown = Observable.FromEvent<RoutedEventArgs>(this.StopButton, "Click");
        startMouseDown
            .SelectMany(down =>
                createUrlObservable()
                    .SelectMany(url => createRequestObservable(url)
                        .TakeUntil(stopMouseDown)
                        .Select(r => r.GetResponseStream())
                        .Do(s =>
                            {
                                using (var sr = new StreamReader(s))
                                {
                                    Trace.WriteLine(sr.ReadLine());
                                    Trace.Flush();
                                }

                            })
                        .Do(r => this._successed++)
                        .HandleError(e => this._failed++))
                        .ObserveOnDispatcher()
                        .Do(_ => this.RefresLabels(),
                            e => { },
                            () => this.RefresLabels())

                        )
            .Subscribe();
public static class ObservableExtensions
{
    public static IObservable<TSource> HandleError<TSource>(this IObservable<TSource> source, Action<Exception> errorHandler)
    {
        return Observable.CreateWithDisposable<TSource>(observer =>
            {
                return source.Subscribe(observer.OnNext, 
                    e => 
                    { 
                        errorHandler(e);
                        //observer.OnError(e);
                        observer.OnCompleted();
                    },
                    observer.OnCompleted);
            });
    }
}
    private void RefresLabels()
    {
        this.SuccessedLabel.Content = string.Format("Successed {0}", this._successed);
        this.FailedLabel.Content = string.Format("Failed {0}", this._failed);
    }