产生多个IEnumebles

时间:2011-08-05 14:10:25

标签: c# stream

我有一段代码可以对资产进行计算。有数百万,所以我想在流中计算所有内容。我目前的“管道”看起来像这样:

我有一个作为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;
   }
}

提前致谢,

格特 - 扬

5 个答案:

答案 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