简体   繁体   English

使用Rx轮询网站

[英]Polling a website using Rx

just trying to get my head around Rx 只是试图让我的头围绕Rx

I am using Rx to poll a website every 2 seconds 我每隔2秒使用Rx来浏览一个网站

var results = new List<MyDTO>();
var cx = new WebserviceAPI( ... );
var callback = cx.GetDataAsync().Subscribe(rs => { results.AddRange(rs); });
var poller = Observable.Interval(TimeSpan.FromSeconds(2)).Subscribe( _ => { cx.StartGetDataAsync(); });

(The webservice API exposes a getItemsAsync/getItemsCompleted event handler type mechanism from which I am creating an observable). (webservice API公开了一个getItemsAsync / getItemsCompleted事件处理程序类型机制,我正在从中创建一个observable)。

When the web site returns, I am unpacking the "business part of" the response into an IEnumerable of DTOs 当网站返回时,我正在将响应的“业务部分”解压缩到IEnumerable的DTO中

public IObservable<IEnumerable<MyDTO>> GetDataAsync()
{
    var o = Observable.FromEventPattern<getItemsCompletedEventHandler,getItemsCompletedEventArgs>(
        h => _webService.getItemsCompleted += h,
        h => _webService.getItemsCompleted -= h);

    return o.Select(c=> from itm in c.EventArgs.Result.ItemList
                        select new MyDTO()
                        {
                           ...
                        });
}

My reasoning being that given that all the data was just there in the string, it made sense just to pack it up there an then into an IEnumerable ... but now I'm not sure if that is right! 我的理由是,鉴于所有数据都在字符串中,只有将它打包到IEnumerable中才有意义......但现在我不确定这是不对的!

If the website takes longer than 2 secs to respond I am finding that MSTest is crashing out. 如果网站响应时间超过2秒,我发现MSTest正在崩溃。 When debugging, the error being generated is 调试时,生成的错误是

"There was an error during asynchronous processing. Unique state object is required for multiple asynchronous simultaneous operations to be outstanding" “异步处理期间出现错误。多个异步同步操作需要独特的状态对象才能出色”

with the inner exception 内在的例外

"Item has already been added. Key in dictionary: 'System.Object' Key being added: 'System.Object'" “项目已被添加。键入字典:'System.Object'键被添加:'System.Object'”

I am supposing that the problem is one of reentrancy in that the next call is starting and returning data before the previous call has finished populating the data. 我假设问题是重入的问题之一是下一个调用是在前一个调用完成填充数据之前启动并返回数据。

So I'm not sure whether 所以我不确定是否

  1. I have put the thing together quite right 我把事情放在一起非常正确
  2. I should be throttling the connection in some way so as to avoid re-entrancy. 我应该以某种方式限制连接,以避免重新入侵。
  3. I should use a different intermediate data structure (or mechanism) instead of an IEnumerable 我应该使用不同的中间数据结构(或机制)而不是IEnumerable

I would appreciate some guidance. 我会很感激一些指导。

EDIT 1: So I have changed the web call to include a unique state object 编辑1:所以我更改了Web调用以包含一个唯一的状态对象

public void StartGetDataAsync()
{
    ...
    //  was: _webService.getItemsAsync(request);
    _webService.getItemsAsync(request, Guid.NewGuid());
}

and made it work. 并使它工作。 But I am still unsure if that is the right way to do it 但我仍然不确定这是否是正确的做法

EDIT 2 - Web service sigs: I'm consuming a soap web service which the webServiceApi class wraps. 编辑2 - Web服务sigs:我正在使用webServiceApi类包装的soap Web服务。 The references.cs created contains the following methods 创建的references.cs包含以下方法

public void getItemsAsync(GetItemsReq request, object userState) 
{
    if ((this.getItemsOperationCompleted == null)) 
    {
        this.getItemsOperationCompleted = new System.Threading.SendOrPostCallback(this.OngetItemsOperationCompleted);
    }
    this.InvokeAsync("getItems", new object[] {
                    request}, this.getItemsOperationCompleted, userState);
}

private System.Threading.SendOrPostCallback getItemsOperationCompleted;

public event getItemsCompletedEventHandler getItemsCompleted;

public delegate void getItemsCompletedEventHandler(object sender, getItemsCompletedEventArgs e);

public partial class getItemsCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs 
{
    ...
}

private void OngetItemsOperationCompleted(object arg) 
{
    if ((this.getItemsCompleted != null)) 
    {
        System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
        this.getItemsCompleted(this, new getItemsCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
    }
 }

Probably given you too much (or missed something)! 可能给你太多(或错过了一些东西)!

Thx 谢谢

I think I've got a decent starting point for you. 我想我有一个不错的起点。

Basically I think you need to abstract away the complexity of the web service and create a nice clean function to get your results. 基本上我认为你需要抽象出Web服务的复杂性并创建一个很好的清理函数来获得结果。

Try something like this: 尝试这样的事情:

Func<GetItemsReq, IObservable<getItemsCompletedEventArgs>> fetch =
    rq =>
        Observable.Create<getItemsCompletedEventArgs>(o =>
        {
            var cx = new WebserviceAPI(/* ... */);
            var state = new object();
            var res =
                Observable
                    .FromEventPattern<
                        getItemsCompletedEventHandler,
                        getItemsCompletedEventArgs>(
                        h => cx.getItemsCompleted += h,
                        h => cx.getItemsCompleted -= h)
                    .Where(x => x.EventArgs.UserState == state)
                    .Take(1)
                    .Select(x => x.EventArgs);
            var subscription = res.Subscribe(o);
            cx.getItemsAsync(rq, state);
            return subscription;
        });

Personally I would go one step further and define a return type, say GetItemsReq , that doesn't include the user state object, but is basically the same as getItemsCompletedEventArgs . 我个人会更进一步,定义一个返回类型,比如GetItemsReq ,它不包含用户状态对象,但与getItemsCompletedEventArgs基本相同。

You should then be able to use Observable.Interval to create the polling that you need. 然后,您应该能够使用Observable.Interval来创建所需的轮询。

If your web service implements IDisposable then you should add an Observable.Using call into the above function to correctly dispose of the web service when it is complete. 如果您的Web服务实现了IDisposable那么您应该在上面的函数中添加一个Observable.Using调用,以便在Web服务完成时正确处理它。

Let me know if this helps. 如果这有帮助,请告诉我。

Enigmativity has a good solution. Enigmativity有一个很好的解决方案。

  var state = new object(); cx.getItemsAsync(rq, state); 

These statements are the key to solving the error. 这些陈述是解决错误的关键。

Each time an Async method is called, it needs to have a unique object passed to it (unless of course only one Async method will be running at a time, which is highly unlikely). 每次调用Async方法时,都需要传递一个唯一的对象(除非当然只有一个Async方法一次运行,这是非常不可能的)。 I scratched my head over this for hours until I discovered that you can pass an object parameter. 我抓了几个小时,直到我发现你可以传递一个对象参数。 Without the parameter, it will see multiple methods as the same call and give you that "Item has already been added. Key in dictionary: 'System.Object' Key being added: 'System.Object'" message. 如果没有参数,它将看到多个方法作为同一个调用,并为您提供“已添加项目。键入字典:'System.Object'键被添加:'System.Object'”消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM