﻿<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>Ayende @ Rahien</title><link>http://ayende.com</link><description>Ayende @ Rahien</description><copyright>Copyright (C) Ayende Rahien  2004 - 2021 (c) 2026</copyright><ttl>60</ttl><item><title>wow commented on Pipes and filters: The IEnumerable appraoch</title><description>wow, I just stumbled upon this and will bookmark this.. my understanding of LINQ to Objects is 10x.  Thank you Thank you!
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment22</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment22</guid><pubDate>Sun, 13 Jan 2008 06:48:03 GMT</pubDate></item><item><title>Jon Skeet commented on Pipes and filters: The IEnumerable appraoch</title><description>Is your example with customers meant to be a single pipeline, or two? There seems to be a disconnect at item 4. If there isn't, I don't quite understand it - but it raises an interesting issue anyway.
  
  
One issue with the "pull" model of LINQ is it assumes there's basically one consumer. If you want to split a pipeline, it's relatively hard to do without threading. Just as a sort of plug, and because you might find it interesting, have a look at my blog entry about "push" LINQ:
  
  
http://msmvps.com/blogs/jon.skeet/archive/2008/01/04/quot-push-quot-linq-revisited-next-attempt-at-an-explanation.aspx
  
  
(Or at the moment, just the top entry at http://msmvps.com/jon.skeet)
  
  
It supports splitting at any point very naturally, although I haven't used that for anything other than simple cases.
  
  
I'd expect all the steps you've mentioned to be feasible with LINQ though - as I say in the fluent pipelines thread, you can still use methods as delegate targets too...
  
  
Jon
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment21</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment21</guid><pubDate>Mon, 07 Jan 2008 08:55:31 GMT</pubDate></item><item><title>Paul Stovell commented on Pipes and filters: The IEnumerable appraoch</title><description>Using LINQ and extension methods does not mean you have to put all of your operation inside one method. 
  
  
If you look at many extensions, they actually return a class (implementing IEnumerble&lt;T&gt;) which contains all the logic. The SyncLINQ source code certainly isn’t one 40,000 line class with 500 massive methods; I’m sure the LINQ source is similar. 
  
  
ETL can't be done with LINQ? Here's how I'd do it:
  
  
var transformedCustomers = new AddAdditionalMetadataToCustomerOperation(
  
                               new ConvertCRMCustomerToSASCustomerOperation(
  
                                  new SwapFirstNameAndLastNameOperation(
  
                                      new ImportCustomersFromCRMOperation(crmUrl));
  
  
The nice thing about IEnumerable and not fixing the inputs/outputs is each class can be defined differently:
  
  
class AddAdditionalMetadataToCustomerOperation  :  IEnumerable&lt;SASCustomer&gt;
  
   public new (IEnumerable&lt;SASCustomer&gt;);
  
  
class ConvertCRMCustomerToSASCustomerOperation  :  IEnumerable&lt;SASCustomer&gt;
  
   public new (IEnumerable&lt;CRMCustomer&gt;);
  
  
class SwapFirstNameAndLastNameOperation:  IEnumerable&lt;CRMCustomer&gt;
  
   public new (IEnumerable&lt;CRMCustomer&gt;);
  
  
class ImportCustomersFromCRMOperation:  IEnumerable&lt;CRMCustomer&gt;
  
   public new (string crmUrl);
  
  
Note that each operation can return different types, and take different types as inputs. Since you’re using classes, you can use inheritance. It has all the capabilities of the solution you blogged about. You can add multithreading, processor yielding, whatever you want. 
  
  
Then, for readability only, you can wrap it in some LINQ extensions to become:
  
  
var sasCustomers = ImportCustomersFromCRM(url)
  
                   .SwapFirstAndLastName()
  
                   .ConvertToSasCustomers()
  
                   .AddAdditonalMetadata();
  
  
And like any good pipeline, it reads from right to left :)
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment20</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment20</guid><pubDate>Mon, 07 Jan 2008 03:27:35 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>1/ if they can't grok the concept in 30 minutes, they are not worth keeping. The idea of grabbing someone from the street is a myth.
  
2/ build a DelegateOperation&lt;T&gt;(Action&lt;T&gt;), and you are set
  
3/ In this scenario, I actually need it to keep one type all the way. I am doing transformations in a pipeline. If I wanted any type, I could have used IEnumerable instead of IEnumerable&lt;T&gt;
  
4/ interesting, probably the best point.
  
  
I said that the print processes example is trivial, right?
  
A more realistic sample would be:
  
  
1/ read customers from file
  
2/ left join to existing customers in database
  
3/ for all those missing from database:
  
   3.1/ create customer record
  
   3.2/ send email about new customer
  
4/ get all active orders in last day
  
5/ left join customers with orders
  
6/ update customer statistics
  
  6.1 / amount bought
  
  6.2 / favorite products
  
7/ update customers
  
  
Assume significant complexity for at least some of those steps.
  
Assume that I want to maintain separation of concerns.
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment19</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment19</guid><pubDate>Sun, 06 Jan 2008 19:11:48 GMT</pubDate></item><item><title>Jon commented on Pipes and filters: The IEnumerable appraoch</title><description>Using LINQ would add four things:
  
  
1) Not reinventing the wheel. If you hire a C# developer in a year, I'd hope they'd be familiar with LINQ. They probably won't be familiar with your pipeline framework.
  
  
2) Taking advantage of the ease of creating delegates in C# 3, rather than forcing the use of interfaces.
  
  
3) Transformation ability, as Paul pointed out, where the input and output types can be different
  
  
4) The ability to integrate simply with other LINQ-related technologies such as Parallel LINQ.
  
  
I would argue that your use of PrintProcessName is an odd one for a pipeline, to yield an empty result at the end. I think I'd rather implement a ForEach extension method on IEnumerable&lt;T&gt; which takes an Action&lt;T&gt;. (I'm kinda surprised that isn't in the framework already, to be honest.)
  
  
At that point, your code would become:
  
  
Process.GetAllProcesses()
  
    .Where (proc =&gt; proc.WorkingSet64 &gt; 50*1024*1024)
  
    .ForEach(proc =&gt; Console.WriteLine(proc.ProcessName);
  
  
No new types needed at all, except for the static class to hold the ForEach extension method. If you *want* to create extra classes for reusable logic, you certainly can - but you don't have to.
  
  
If the logic for any of the steps is complicated, you can stick that in a method easily, either casting the method group or just calling the method from a lambda expression. When the logic *isn't* complicated, do it inline as above.
  
  
Personally, I think that's more maintainable. We still have the composability and the streaming, but we *also* have all the standard query operators, the ability to use query expressions where you need to, etc.
  
  
I may have said it before on this blog, but I believe LINQ to Objects has been significantly under-marketed. LINQ to SQL has more of an "ooh, ahh" factor - but LINQ to Objects will be more applicable in many situations (and without forcing you to use SQL server!)
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment18</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment18</guid><pubDate>Sun, 06 Jan 2008 18:54:21 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>Jon,
  
The only criteria that I have for this is how maintainable I can make it.
  
I don't see Linq adding anything to the mix here. It is possible that I am wrong, but I would wait to see the code before being able to say so.
  
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment17</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment17</guid><pubDate>Sun, 06 Jan 2008 18:40:02 GMT</pubDate></item><item><title>Jon Skeet commented on Pipes and filters: The IEnumerable appraoch</title><description>If you want more than a one liner, write a method and use that as the action of the delegate instance. Don't forget:
  
  
1) You don't have to use anonymous methods or lambda expressions to create delegate instances.
  
2) You don't have to use query expressions to use LINQ.
  
3) You can write your own extension methods as well to expand LINQ as you need to.
  
  
Now admittedly the bug wrt output type inference of method groups is a slight disadvantage here - but it's better than being forced to (manually) create a new type every time you need a different kind of filter or transformation.
  
  
I really believe that pretty much any limitation of LINQ is going to prove a limitation of your scheme above too - simply because they're so similar. The advantage of LINQ is that for simple cases you *can* use query expressions, lambda expressions etc. Oh, and it's going to be rather better understood by the majority of developers in the next couple of years :)
  
  
Jon
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment16</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment16</guid><pubDate>Sun, 06 Jan 2008 16:02:26 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>Jon,
  
When I am thinking about selecting &amp; transforming, I am usually talking about more than one liners.
  
Assume that you want to interrogate an external system for data about the customer credit status.
  
Or that the transformation is involved or contains complex business logic.
  
  
All I have seen of linq so far convinced me that it breaks down really fast when you get to complex stuff.
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment15</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment15</guid><pubDate>Sun, 06 Jan 2008 15:04:04 GMT</pubDate></item><item><title>Jon Skeet commented on Pipes and filters: The IEnumerable appraoch</title><description>Business rules deciding on whether or not an order batch should be approved: Where clause.
  
  
Transformation: Select clause
  
  
Data manipulation for a row in an ETL process: I don't know enough about this to comment.
  
  
The first two at least are perfectly reasonable uses for LINQ, and I suspect the last is too. LINQ is for more than just querying. Paul is right: pipes and filters are exactly what LINQ is all about, at least for LINQ to Objects.
  
  
Jon
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment14</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment14</guid><pubDate>Sun, 06 Jan 2008 14:59:48 GMT</pubDate></item><item><title>Markus Zywitza commented on Pipes and filters: The IEnumerable appraoch</title><description>For better composibility, you should consider Pipeline&lt;T&gt; implementing IOperation&lt;T&gt; to allow concatenating Pipelines.
  
The Execute()-Method will then use the input enumeration instead of creating a new List and yield its results, avoiding the ugly empty while-loop as a side benefit.
  
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment13</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment13</guid><pubDate>Sun, 06 Jan 2008 12:29:36 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>Paul,
  
Consider a set of business rules that need to execute on an order batch, to decide if we can approve it.
  
Consider a set of transformations that a message goes through before it is let out the door.
  
Consider a set of data manipulation that occurs for a row in an ETL process.
  
  
Linq is not a good approach in those scenarios. I am not talking about querying, in most cases, I am talking about processing.
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment12</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment12</guid><pubDate>Sun, 06 Jan 2008 07:28:03 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>Alex,
  
Because of the filtering thing.
  
I may want to stop processing an item, so I can just not yield it.
  
I may want to split an item, so I can just yield it twice.
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment11</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment11</guid><pubDate>Sun, 06 Jan 2008 07:25:04 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>Omer,
  
A class give me more options and has better scalability in general.
  
You could do it with a delegate, but using classes makes sense in this scenario, I _want_ to have a lot of small tiny classes.
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment10</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment10</guid><pubDate>Sun, 06 Jan 2008 07:23:39 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>Arnon,
  
Thanks for pointing it out.
  
It looks like I am calling filters a state or operation, is that what you mean?
  
I don't like the name filter in this case, because most of the time I am not doing filtering there, I am doing transformations on the data.
  
  
Note to self: re-read pattern's description before using it.
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment9</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment9</guid><pubDate>Sun, 06 Jan 2008 07:22:01 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>Avish,
  
yes, we are cheating the compiler to get the nice programming model.
  
I mentioned that this is a trivial example. The real ones are usually too complex to be easily explained.
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment8</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment8</guid><pubDate>Sun, 06 Jan 2008 07:19:00 GMT</pubDate></item><item><title>Paul Stovell commented on Pipes and filters: The IEnumerable appraoch</title><description>Hi Oren,
  
  
Pipes and filters are exactly what LINQ does. 
  
  
Instead of implementing an IOperation&lt;T&gt; interface, you could simply implement IEnumerable&lt;T&gt;. The following extension methods:
  
  
public static IEnumerable&lt;Process&gt; LimitByWorkingSetSize(this IEnumerable&lt;Process&gt; inputs);
  
public static IEnumerable&lt;Process&gt; PrintProcessName(this IEnumerable&lt;Process&gt; inputs);
  
  
Would be all you need. You can then pipe them like so:
  
  
GetAllProcesses().LimitByWorkingSetSize().PrintProcessName();
  
  
The Pipeline&lt;T&gt; class also has a severe limitation in that it assumes all operations are for the same type. This disables transformation features within an operation. The Pipeline&lt;T&gt; class should be simply Pipeline, and the operation should at least be:
  
  
IOperation&lt;TInput, TOutput&gt;
  
  
However, what if an operation has multiple inputs? (Like a union for example.) You could collect all of the inputs into one container object. But why not simply use a method, IEnumerable&lt;T&gt;, and skip the IOperation&lt;T&gt; interface? 
  
  
static IEnumerable&lt;Process&gt; LimitByWorkingSetSize(this IEnumerable&lt;Process&gt; inputs, int size) {
  
    return new LimitByWorkingSetSizeEnumerator(inputs, size);
  
}
  
  
class LimitByWorkingSetSizeEnumerator : IEnumerable&lt;Process&gt;{ 
  
   private int _size;
  
   private IEnumerable&lt;Process&gt; _inputs;
  
  
   public LimitByWorkingSetSize(IEnumerable&lt;Process&gt; inputs, int size) {
  
         _size = size; _inputs = inputs;
  
    }
  
  
   public void GetEnumerator() {
  
        foreach (Process p in inputs) {
  
             if (p.Size &lt; _size) {
  
                 yield return p;
  
             }
  
        }
  
    }
  
}
  
  
Note that the implementations can be stateful or stateless. Consider the "Where" extension, which filters items one-by-one, or the OrderBy extension, which reads all of the inputs before sorting and returning the outputs. 
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment7</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment7</guid><pubDate>Sun, 06 Jan 2008 00:31:26 GMT</pubDate></item><item><title>Alex Henderson commented on Pipes and filters: The IEnumerable appraoch</title><description>Hi Oren,
  
  
The thing that bugs me is the IEnumerable&lt;T&gt; being baked in - why not make the operations work with T instead of IEnumerable&lt;T&gt;, you can still achieve the same end result, but you can use the pipeline for processing singular items like requests... or am I missing something?
  
  
Maybe something like this:
  
  
http://trac.devdefined.com/public/trac/tools.devdefined.com/browser/trunk/src/DevDefined.Common/Pipeline/Pipeline.cs
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment6</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment6</guid><pubDate>Sun, 06 Jan 2008 00:10:38 GMT</pubDate></item><item><title>Omer Mor commented on Pipes and filters: The IEnumerable appraoch</title><description>Nice implementation, Oren.
  
Is there any reason you chose to use an interface (IOperation) instead of a delegate?
  
I don't like decalring new classes if I don't have to, and I don't see any problem with replacing IOperation with a method that fits the following delegate:
  
delegate IEnumerable&lt;Process&gt; ExecuteOperation(IEnumerable&lt;Process&gt; input);
  
  
If you'll need a statefull class that contains the operation that you can implement one and pass its execute method as the delegate, but if your operations are stateless, than a simple (possibly static) method will do.
  
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment5</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment5</guid><pubDate>Sat, 05 Jan 2008 23:26:01 GMT</pubDate></item><item><title>Arnon Rotem-Gal-Oz commented on Pipes and filters: The IEnumerable appraoch</title><description>Hi Oren
  
You got the pipes and filters mixed
  
The pipes are where the messages flow and the filters is where the processing is done (see http://www.enterpriseintegrationpatterns.com/PipesAndFilters.html)
  
  
Arnon
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment4</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment4</guid><pubDate>Sat, 05 Jan 2008 22:12:32 GMT</pubDate></item><item><title>Avish commented on Pipes and filters: The IEnumerable appraoch</title><description>That's nice, but I'm bugged about the way the ends are implemented. The first step ignores its input, and in the last step we had to trick the compiler since we're not returning anything. Also, the last "while (enumerator.MoveNext())" is a little ugly.
  
  
Also, I think the example doesn't do a good job of explaining the need. This kind of things really calls for list comprehensions (p.Name for p in Process.GetProcesses if p.WorkingSet &gt; threshold) or LINQ, I guess. This heavy-duty kind of pipelines really is useful for doing complicated, state-sensitive work on a collection of objects (the compiler example is a better one). 
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment3</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment3</guid><pubDate>Sat, 05 Jan 2008 20:57:27 GMT</pubDate></item><item><title>Ayende Rahien commented on Pipes and filters: The IEnumerable appraoch</title><description>I gave a few in the beginning of the post.
  
Others include batch processing, ETL, workflow, etc.
  
  
The last two lines are where the magic happens, they are driving the whole thing.
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment2</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment2</guid><pubDate>Sat, 05 Jan 2008 20:43:49 GMT</pubDate></item><item><title>Mark Monster commented on Pipes and filters: The IEnumerable appraoch</title><description>I've heard about Pipes and Filters as an Architecture Pattern. But didn't do any implementation of it yet. Do you have a real world example about when Pipes and Filters is a good use?
  
  
Besides this, a very nice Pipes and Filters solution I think. I haven't tried the code, yet. But what about the last two lines in Pipeline.Execute, what's the use?
  
IEnumerator&lt;T&gt; enumerator = current.GetEnumerator();
  
while (enumerator.MoveNext()) ;
</description><link>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment1</link><guid>http://ayende.com/3082/pipes-and-filters-the-ienumerable-appraoch#comment1</guid><pubDate>Sat, 05 Jan 2008 20:39:57 GMT</pubDate></item></channel></rss>