Rx新手 - 我有一个似乎正常运作的序列,除了它似乎重复的事实。
我想我错过了调用Select()
或SelectMany()
的内容,触发范围重新评估。
x
个(根据设置)。我认为这与从0开始重新评估范围的调用有关,但我无法弄清楚它是什么。
var query = Observable.Range(0, int.MaxValue)
.Select(pageNum =>
{
_etlLogger.Info("Calling GetResProfIDsToProcess with pageNum of {0}", pageNum);
return _recordsToProcessRetriever.GetResProfIDsToProcess(pageNum, _processorSettings.BatchSize);
})
.TakeWhile(resProfList => resProfList.Any())
.SelectMany(records => records.Where(x=> _determiner.ShouldProcess(x)))
.Select(resProf => Observable.Start(async () => await _schoolDataProcessor.ProcessSchoolsAsync(resProf)))
.Merge(maxConcurrent: _processorSettings.ParallelProperties)
.Do(async trackingRequests =>
{
await CreateRequests(trackingRequests.Result, createTrackingPayload);
var numberOfAttachments = SumOfRequestType(trackingRequests.Result, TrackingRecordRequestType.AttachSchool);
var numberOfDetachments = SumOfRequestType(trackingRequests.Result, TrackingRecordRequestType.DetachSchool);
var numberOfAssignmentTypeUpdates = SumOfRequestType(trackingRequests.Result,
TrackingRecordRequestType.UpdateAssignmentType);
_etlLogger.Info("Extractor generated {0} attachments, {1} detachments, and {2} assignment type changes.",
numberOfAttachments, numberOfDetachments, numberOfAssignmentTypeUpdates);
});
var subscription = query.Subscribe(
trackingRequests =>
{
//Nothing really needs to happen here. Technically we're just doing something when it's done.
},
() =>
{
_etlLogger.Info("Finished! Woohoo!");
});
await query.Wait();
答案 0 :(得分:1)
这是因为您订阅了两次序列。在query.Subscribe(...)
后再次在query.Wait()
。
Observable.Range(0, int.MaxValue)
是一个冷酷的观察者。每次订阅时,都会再次进行评估。您可以通过Publish()
发布,然后订阅它,然后Connect()
然后Wait()
来使可观察的热点变为热点。如果在已经产生最后一个元素之后调用InvalidOperationException
,则会增加获得Wait()
的风险。更好的选择是LastOrDefaultAsync()
。
那会让你得到这样的东西:
var connectable = query.Publish();
var subscription = connectable.Subscribe(...);
subscription = new CompositeDisposable(connectable.Connect(), subscription);
await connectable.LastOrDefaultAsync();
或者您可以避免等待并直接使用ToTask()
返回任务(从方法签名中删除异步)。
return connectable.LastOrDefaultAsync().ToTask();
转换为任务后,您可以使用Wait()
同步等待该任务(不要将Task.Wait()
与Observable.Wait()
混淆)。
connectable.LastOrDefaultAsync().ToTask().Wait();
然而,很可能你根本不想等!等待异步上下文毫无意义。你应该做什么把在序列完成后需要运行的剩余代码放在订阅的OnComplete()
部分。如果您有(清理)代码,即使您取消订阅(Dispose)也需要运行,请考虑Observable.Using
或Finally(...)
方法以确保运行此代码。
答案 1 :(得分:1)
正如已经提到的,重复<a>
的原因是您订阅了两次 - 一次使用Observable.Range
,一次使用.Subscribe(...)
。
在这种情况下,我会使用一个非常简单的阻塞调用来获取值。就这样做:
.Wait()
var results = query.ToArray().Wait();
将多值.ToArray()
转换为单个值IObservable<T>
。 IObservable<T[]>
将其转换为.Wait()
。这是确保只有一个订阅,阻止和获取所有值的简单方法。
在你的情况下,你可能不需要所有的价值观,但我认为这是一个很好的习惯。