Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/297.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 管道模式和一次性对象_C#_.net_Design Patterns_Pipeline - Fatal编程技术网

C# 管道模式和一次性对象

C# 管道模式和一次性对象,c#,.net,design-patterns,pipeline,C#,.net,Design Patterns,Pipeline,最近我开始研究一种管道模式,也称为管道和过滤器。我认为这是一个很好的方法来构造只处理数据的代码和应用程序。 我将此作为管道和步骤实现的基础(但这并不重要)。 像往常一样,博客涵盖了简单的场景,但就我而言,我需要(或者可能不需要)处理IDisposable对象,这些对象可能会在整个过程中传播 比如溪流 让我们考虑一个简单的流水线,它应该加载CSV文件并把它的行插入到一些DB中。在简单的抽象中,我们可以实现这样的功能 Stream Step1(string filePath) IEnumerable

最近我开始研究一种
管道
模式,也称为
管道和过滤器
。我认为这是一个很好的方法来构造只处理数据的代码和应用程序。 我将此作为管道和步骤实现的基础(但这并不重要)。 像往常一样,博客涵盖了简单的场景,但就我而言,我需要(或者可能不需要)处理
IDisposable
对象,这些对象可能会在整个过程中传播

比如溪流

让我们考虑一个简单的流水线,它应该加载CSV文件并把它的行插入到一些DB中。在简单的抽象中,我们可以实现这样的功能

Stream Step1(string filePath)
IEnumerable<RowType> Step2(Stream stream)
bool Step3(IEnumerable<RowType> data)
流步骤1(字符串文件路径)
IEnumerable步骤2(流)
布尔步骤3(IEnumerable数据)
现在我的问题是,这是否是一个好办法。因为如果我们一步一步地实现,那么
对象离开第一步,很容易陷入内存泄漏问题。 我知道有些人可能会说我应该有
Step1
,这是加载和反序列化数据,但我们正在考虑简单的过程。我们可能会遇到更复杂的情况,其中通过一条流更有意义


我想知道如何实现这样的管道,以避免内存泄漏,并避免将整个文件加载到
MemoryStream
(这样更安全)。我是否应该以某种方式将每个步骤包装在
try..catch
块中,以便在出现问题时调用
Dispose()
?或者我应该将所有
IDisposable
资源传递到
Pipeline
对象中,该对象将使用
包装,以正确处理处理过程中生成的所有资源吗?

如果计划像
步骤3(步骤2(步骤1(文件路径)))那样使用
,那么
Step2
应处理流。它可以使用c#的
yield return
功能,该功能创建一个under的实现,该实现实现了
IDisposable
,并允许“订阅”完成枚举和调用
流的“事件”。此时,Dispose
。例如:

IEnumerable<RowType> Step2(Stream stream)
{
    using(stream)
    using(StreamReader sr = new StreamReader(stream))
    {
        while(!sr.EndOfStream)
        {
           yield return Parse(sr.ReadLine()); //yield return implements IEnumerator<>
        }
    } // finally part of the using will be called from IEnumerator<>.Dispose()        
}
foreach

bool Step3(IEnumerable<RowType> data)
{
    foreach(var item in data)
        if(SomeDecisionLogic(item)))
            return true;
}
bool步骤3(IEnumerable数据)
{
foreach(数据中的var项)
if(某些决策逻辑(项)))
返回true;
}

对于枚举,它们都保证调用
IEnumerator.Dispose()
(,),这将调用
Stream.Dispose

如果交互在至少两个不同的系统之间,并且工作可以并行执行,那么拥有一个管道是值得的。否则会增加开销

在这种情况下,有两个系统:CSV文件所在的文件系统和数据库。我认为管道应该至少有两个并行运行的步骤:

IEnumerable<Row> ReadFromCsv(string csvFilePath)
void UpdateDabase<IEnumerable<Row> rows)

我猜范围取决于步骤,而步骤又取决于您根据需要设计管道的方式。

可能需要重新考虑该模式在这种情况下是否有用。您可以编写此命令将数据反序列化到集合中,然后将项目插入数据库。那是两门课。你一个接一个地调用它们。即使我使用某种管道,我仍然会将它们隔离,并从管道步骤调用它们。因此,这就提出了一个问题——在这种情况下,将它们安排在管道中增加了什么?看起来我们可以在管道中安排任何顺序步骤,但我们应该吗?@ScottHannen基本上如果我使用这种模式,那么与团队其他成员的沟通会更容易——每个成员都有不同的经验。因此,准备很少的接口并告诉团队“我们应该使用管道模式”是一个很大的优势。这样,设计步骤和在开发人员之间共享工作就很容易了。我完全同意你的看法。若我想在项目中完成这项工作,那个么我不介意。虽然界面共享工作非常棒,但你们不需要管道来完成这项工作。这甚至可能让意图变得不那么清晰。可以将两个抽象定义为接口:一个反序列化,另一个写入数据库。应用程序依赖于这两种抽象。然后不同的开发人员可以在他们的实现上工作。我还没用过,所以我的答案和这个相比可能完全错了。谢谢。正如我刚才所说。我觉得对这些过程使用模式有助于团队更好地沟通和分享工作。您的观点非常有用谢谢,看起来在步骤之外传递流不是问题-我们只需要确保在任何场景中都不会忘记处理它是的,并且
foreach
/
LINQ
方法会在
IEnumerator
上自动调用
dispose
IEnumerable<Row> ReadFromCsv(string csvFilePath)
void UpdateDabase<IEnumerable<Row> rows)
IEnumerable<Row> ReadFromCsv(path)
{
  using(var stream = File.OpenRead(path))
  {
      var lines = GetLines(stream); // yield one at a time, not all at once
      foreach (var line in line) yield return GetRow(line);
  }
}