我有一段代码可以对资产进行计算。有数百万,所以我想在流中计算所有内容。我目前的“管道”看起来像这样:
我有一个作为Datareader执行的查询。
然后我的Asset类有一个接受IDataReader的构造函数;
Public Asset(IdataReader rdr){
// logic that initiates fields
}
以及将IDataReader转换为IEnumerable< Asset>
的方法public static IEnumerable<Asset> ToAssets(IDataReader rdr) {
// make sure the reader is in the right formt
CheckReaderFormat(rdr);
// project reader into IEnumeable<Asset>
while (rdr.Read()) yield return new Asset(rdr);
}
然后将其传递给执行实际计算的函数,然后将其投影到IEnumerable&lt; Asnwer&gt;
然后得到一个包装器,将Answers公开为IDataReader,然后将其传递给OracleBulkCopy,并将流写入数据库。
到目前为止,它的作用就像一个魅力。由于设置,我可以将DataReader交换为从文件读取的IEnumerable,或将结果写入文件等。所有这些都取决于我如何将类/函数串在一起。
现在:有几件事我可以计算,例如除了正常的答案我可以有一个DebugAnswer类,它也输出一些中间数字进行调试。所以我想做的是将IEnumerable投影到几个输出流中,这样我就可以把'听众'放在那些上。这样我就不必多次查看数据了。我怎样才能做到这一点?有点像有几个事件,然后只有附加了一个听众才会触发某些代码。
有时我会写入数据库,但也会写入zip文件,以保留结果的备份。那么我想在IEnumerable上有2个'听众'。一个项目是IDataReader,另一个项目直接写入文件。
如何输出多个输出流以及如何在一个输出流上放置多个侦听器?是什么让我组合这样的数据流?
修改
所以我想做的一些伪代码:
foreach(Asset in Assets){
if(DebugListener != null){
// compute
DebugAnswer da = new DebugAnswer {result = 100};
yield da to DebugListener; // so instead of yield return yield to that stream
}
if(AnswerListener != null){
// compute basic stuff
Answer a = new Answer { bla = 200 };
yield a to AnswerListener;
}
}
提前致谢,
格特 - 扬
答案 0 :(得分:5)
您所描述的内容听起来有点类似于Reactive框架通过IObservable
界面提供的内容,但我不确定它是否允许多个订阅者使用单个订阅流。
如果你看看documentation for IObservable
,它有一个非常好的例子,说明如何做一些你正在做的事情,有一个对象的多个订阅者。
答案 1 :(得分:4)
您的示例使用Rx重写:
// The stream of assets
IObservable<Asset> assets = ...
// The stream of each asset projected to a DebugAnswer
IObservable<DebugAnswer> debugAnswers = from asset in assets
select new DebugAnswer { result = 100 };
// Subscribe the DebugListener to receive the debugAnswers
debugAnswers.Subscribe(DebugListener);
// The stream of each asset projected to an Anwer
IObservable<Answer> answers = from asset in assets
select new Answer { bla = 200 };
// Subscribe the AnswerListener to receive the answers
answers.Subscribe(AnswerListener);
答案 2 :(得分:1)
这正是Reactive Extensions的工作(从4.0开始成为.NET的一部分,在3.5中作为库提供)。
答案 3 :(得分:1)
您不需要多个“侦听器”,您只需要管道组件,这些组件不具有破坏性,甚至无法转换。
IEnumerable<T> PassThroughEnumerable<T>(IEnumerable<T> source, Action<T> action) {
foreach (T t in source) {
Action(t);
yield return t;
}
}
或者,当您在管道中处理时,只需要引发一些要消耗的事件。如果您愿意,可以将它们异步化:
static IEnumerable<Asset> ToAssets(IDataReader rdr) {
CheckReaderFormat(rdr);
var h = this.DebugAsset;
while (rdr.Read()) {
var a = new Asset(rdr);
if (h != null) h(a);
yield return a;
}
}
public event EventHandler<Asset> DebugAsset;
答案 4 :(得分:0)
如果我找对你,应该可以替换或decorate包装。 WrapperDecorator
可以将调用转发到正常OracleBulkCopy
(或者您正在使用的任何内容)并添加一些自定义调试代码。
这对你有帮助吗?
的Matthias