我第一次尝试通过 System.Reactive
使用 observable。
我希望将生成的 3 个特征的结果推送到同一个 ReportBuilder
,然后在完成时通知调用者,以便调用者可以将生成的 Report
传递给另一个服务。
我面临一些问题:
如果我有一个观察者实现了多种类型的 IObserver,将调用 OnCompleted 方法来完成第一个观察到的序列,不是吗?如何观察多个不同类型的序列直到全部完成?
注册我的订阅后,执行流程是返回到调用者对象还是仅在调用 OnCompleted 之后?这个我不太清楚。
由于构建器既是可观察者又是观察者,我的实现对我来说似乎很尴尬,实现既是可观察者又是观察者的对象的正确方法是什么?
我是否应该合并我的 3 个 observable 并使特征异步以在产生结果时使用它们?如果是这样,如何,因为我的收藏是不同类型的?此外我会消耗三个任务结果的序列,所以我仍然会一个接一个地消耗特征,不是吗?
这是将我的想法写成代码的尝试,但除了提到的问题之外,与直接调用和延迟执行相比,实现似乎没有太多好处。
public interface IFeatureC
{
IEnumerable<ModelC> ExecuteFeature();
}
public interface IFeatureB
{
IEnumerable<ModelB> ExecuteFeature();
}
public interface IFeatureA
{
IEnumerable<ModelA> ExecuteFeature();
}
public class ModelA { }
public class ModelB { }
public class ModelC { }
public class Report
{
public Report()
{
ReportElements = new List<ReportElement>();
}
public List<ReportElement> ReportElements { get; }
}
public class ReportElement
{
public ReportElement(ModelA item)
{
// DoSomething
}
public ReportElement(ModelB item)
{
// DoSomething
}
public ReportElement(ModelC item)
{
// DoSomething
}
}
public interface IOtherService
{
void DoSomething(Report report);
}
public class Orchestrator : IObserver<Report>
{
private readonly ReportBuilder _builder;
private readonly IOtherService _otherService;
private readonly IFeatureA _featureA;
private readonly IFeatureB _featureB;
private readonly IFeatureC _featureC;
private readonly ILogger _logger
public Orchestrator(IFeatureA featureA, IFeatureB featureB, IFeatureC featureC, ReportBuilder builder, IOtherService otherService, ILogger logger)
{
_featureA = featureA;
_featureB = featureB;
_featureC = featureC;
_builder = builder;
_otherService = otherService;
_logger = logger;
}
public void Run()
{
IObservable<ModelA> resultAObservable = _featureA.ExecuteFeature().ToObservable();
IObservable<ModelB> resultBObservable = _featureB.ExecuteFeature().ToObservable();
IObservable<ModelC> resultCObservable = _featureC.ExecuteFeature().ToObservable();
resultAObservable.Subscribe(_builder);
resultBObservable.Subscribe(_builder);
resultCObservable.Subscribe(_builder);
// Do I need to wait until OnCompleted is called ?
}
public void OnCompleted()
{
_logger.Information("Report pushed");
}
public void OnError(Exception error)
{
logger.Error(error);
throw error;
}
public void OnNext(Report report)
{
_otherService.DoSomething(report);
}
}
public class ReportBuilder : IObserver<ModelA>, IObserver<ModelB>, IObserver<ModelC>
{
private readonly IObserver<Report> _reportObserver;
private Report Report { get; } = new Report();
private readonly IObservable<Report> _observableReport;
public ReportBuilder(IObserver<Report> reportObserver) // Is there a better than passing the caller ?
{
_reportObserver = reportObserver;
_observableReport = Observable.Return(Report);
}
public void OnCompleted() // This will get called by the first finished sequence and I will miss some observed elements ?
{
_observableReport.Subscribe(_reportObserver);
}
public void OnError(Exception error)
{
throw error;
}
public void OnNext(ModelA value)
{
Report.ReportElements.Add(new ReportElement(value));
}
public void OnNext(ModelB value)
{
Report.ReportElements.Add(new ReportElement(value));
}
public void OnNext(ModelC value)
{
Report.ReportElements.Add(new ReportElement(value));
}
}
答案 0 :(得分:1)
如果我有一个观察者实现了多种类型的 IObserver,那么将调用 OnCompleted 方法来完成第一个观察到的序列,不是吗?
实现 IObservable<T>
或 IObserver<T>
的情况非常罕见 - 通常最好只使用现有的运算符和类型。如果你自己动手,很难把这些东西弄好 - 正如你发现的那样。
如何观察多个不同类型的序列直到全部完成?
不要实现这样的类。
<块引用>注册我的订阅后,执行流程是返回到调用者对象还是仅在调用 OnCompleted 之后?这个我不太清楚。
执行通常返回给调用者,但这取决于可观察对象。例如,Observable.Return(42).Subscribe(x => Console.WriteLine(x))
在当前线程上执行,因此它将在返回给调用者之前完成可观察对象。这个问题会有所帮助:What are the default Schedulers for each observable operator?
由于构建器既是可观察者又是观察者,我的实现对我来说似乎很尴尬,实现既是可观察者又是观察者的对象的正确方法是什么?
你也没有实施。
<块引用>我是否应该合并我的 3 个 observable 并使特征异步以在生成结果时使用它们?
是和否。合并,但不要担心异步。 Observable 很好。
<块引用>如果是这样,如何,因为我的收藏是不同的类型?
使用 Select
使它们成为一种类型。
此外我会消耗三个任务结果的序列,所以我仍然会消耗一个又一个特征,不是吗?
没有。您可以一次完成所有操作。
以下是您应该如何实现所有代码:
public IObservable<Report> Run(IFeatureA featureA, IFeatureB featureB, IFeatureC featureC) =>
Observable
.Merge(
featureA.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),
featureB.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),
featureC.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)))
.ToArray()
.Select(xs =>
{
var report = new Report();
report.ReportElements.AddRange(xs);
return report;
});
就是这样。
或者,您可以在不失去 Rx 优势的情况下使其同步或异步:
public Report Run(IFeatureA featureA, IFeatureB featureB, IFeatureC featureC) =>
Observable
.Merge(
featureA.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),
featureB.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),
featureC.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)))
.ToArray()
.Select(xs =>
{
var report = new Report();
report.ReportElements.AddRange(xs);
return report;
})
.Wait();
public async Task<Report> RunAsync(IFeatureA featureA, IFeatureB featureB, IFeatureC featureC) =>
await Observable
.Merge(
featureA.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),
featureB.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),
featureC.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)))
.ToArray()
.Select(xs =>
{
var report = new Report();
report.ReportElements.AddRange(xs);
return report;
});