├── .gitignore ├── .nuget ├── NuGet.Config └── NuGet.exe ├── FluentDataflow.Tests.Console ├── App.config ├── FluentDataflow.Tests.Console.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── FluentDataflow.Tests.UnitTests ├── DataflowWrapperTests.cs ├── FluentDataflow.Tests.UnitTests.csproj ├── MultipleDataflowWrapperTests.cs ├── MultipleSourceDataflowBuilderTests.cs ├── PropagatorDataflowBuilderTests.cs ├── PropagatorDataflowWrapperTests.cs ├── Properties │ └── AssemblyInfo.cs ├── SourceDataflowBuilderTests.cs ├── SourceDataflowWrapperTests.cs ├── TargetDataflowWrapperTests.cs └── packages.config ├── FluentDataflow.sln ├── FluentDataflow ├── Assembly.cs ├── BroadcastDataflowBuilder.cs ├── BroadcastDataflowWrapper.cs ├── DataflowBatchOptions.cs ├── DataflowBuilder.cs ├── DataflowBuilderExtenions.cs ├── DataflowDefaultOptions.cs ├── DataflowFactory.cs ├── DataflowFactoryExtensions.cs ├── DataflowJoinOptions.cs ├── DataflowOptionsExtensions.cs ├── DataflowWrapper.cs ├── DataflowWriteOnceOptions.cs ├── FluentDataflow.csproj ├── FluentDataflow.nuspec ├── IDataflowBuilder.cs ├── IDataflowFactory.cs ├── LinkHelper.cs ├── MultipleSourceDataflowBuilder.cs ├── MultipleSourceDataflowWrapper.cs ├── PropagatorDataflowBuilder.cs ├── PropagatorDataflowWrapper.cs ├── ReceivablePropagatorDataflowWrapper.cs ├── ReceivableSourceDataflowWrapper.cs ├── SourceDataflowBuilder.cs ├── SourceDataflowWrapper.cs ├── TargetDataflowBuilder.cs └── TargetDataflowWrapper.cs ├── FluentDataflowPortable ├── FluentDataflowPortable.csproj ├── FluentDataflowPortable.sln ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── LICENSE ├── README.md ├── build.ps1 ├── pack.ps1 └── push.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Visual Studio 2015 cache/options directory 25 | .vs/ 26 | # Uncomment if you have tasks that create the project's static files in wwwroot 27 | #wwwroot/ 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | # NUNIT 34 | *.VisualState.xml 35 | TestResult.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | # DNX 43 | project.lock.json 44 | artifacts/ 45 | 46 | *_i.c 47 | *_p.c 48 | *_i.h 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.tmp_proj 63 | *.log 64 | *.vspscc 65 | *.vssscc 66 | .builds 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # Chutzpah Test files 72 | _Chutzpah* 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opendb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | # NuGet v3's project.json files produces more ignoreable files 154 | *.nuget.props 155 | *.nuget.targets 156 | 157 | # Microsoft Azure Build Output 158 | csx/ 159 | *.build.csdef 160 | 161 | # Microsoft Azure Emulator 162 | ecf/ 163 | rcf/ 164 | 165 | # Microsoft Azure ApplicationInsights config file 166 | ApplicationInsights.config 167 | 168 | # Windows Store app package directory 169 | AppPackages/ 170 | BundleArtifacts/ 171 | 172 | # Visual Studio cache files 173 | # files ending in .cache can be ignored 174 | *.[Cc]ache 175 | # but keep track of directories ending in .cache 176 | !*.[Cc]ache/ 177 | 178 | # Others 179 | ClientBin/ 180 | ~$* 181 | *~ 182 | *.dbmdl 183 | *.dbproj.schemaview 184 | *.pfx 185 | *.publishsettings 186 | node_modules/ 187 | orleans.codegen.cs 188 | 189 | # RIA/Silverlight projects 190 | Generated_Code/ 191 | 192 | # Backup & report files from converting an old project file 193 | # to a newer Visual Studio version. Backup files are not needed, 194 | # because we have git ;-) 195 | _UpgradeReport_Files/ 196 | Backup*/ 197 | UpgradeLog*.XML 198 | UpgradeLog*.htm 199 | 200 | # SQL Server files 201 | *.mdf 202 | *.ldf 203 | 204 | # Business Intelligence projects 205 | *.rdl.data 206 | *.bim.layout 207 | *.bim_*.settings 208 | 209 | # Microsoft Fakes 210 | FakesAssemblies/ 211 | 212 | # GhostDoc plugin setting file 213 | *.GhostDoc.xml 214 | 215 | # Node.js Tools for Visual Studio 216 | .ntvs_analysis.dat 217 | 218 | # Visual Studio 6 build log 219 | *.plg 220 | 221 | # Visual Studio 6 workspace options file 222 | *.opt 223 | 224 | # Visual Studio LightSwitch build output 225 | **/*.HTMLClient/GeneratedArtifacts 226 | **/*.DesktopClient/GeneratedArtifacts 227 | **/*.DesktopClient/ModelManifest.xml 228 | **/*.Server/GeneratedArtifacts 229 | **/*.Server/ModelManifest.xml 230 | _Pvt_Extensions 231 | 232 | # Paket dependency manager 233 | .paket/paket.exe 234 | 235 | # FAKE - F# Make 236 | .fake/ 237 | 238 | pingme.txt 239 | *.sqlite* -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teddymacn/FluentDataflow/e281e66fb756a1607323d7cd64e5fde2274cd0b3/.nuget/NuGet.exe -------------------------------------------------------------------------------- /FluentDataflow.Tests.Console/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.Console/FluentDataflow.Tests.Console.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {1B97E995-3FCF-4356-BCEA-91F254D6FA66} 8 | Exe 9 | FluentDataflow.Tests.Console 10 | FluentDataflow.Tests.Console 11 | v4.6.1 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | ..\packages\System.Threading.Tasks.Dataflow.4.8.0\lib\netstandard2.0\System.Threading.Tasks.Dataflow.dll 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | {d339ef89-bfcf-47f1-b618-a8fcd382649b} 58 | FluentDataflow 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.Console/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace FluentDataflow.Tests.Console 7 | { 8 | class Program 9 | { 10 | private static readonly IDataflowFactory _factory = new DataflowFactory(); 11 | 12 | static void Main(string[] args) 13 | { 14 | TestIfElseBranchingAndMerging(); 15 | System.Console.ReadLine(); 16 | } 17 | 18 | private static ITargetBlock GetAggregatorFlow(out Dictionary result) 19 | { 20 | var splitter = new TransformBlock>(input => 21 | { 22 | string[] splitted = input.Split('='); 23 | return new KeyValuePair(splitted[0], int.Parse(splitted[1])); 24 | }); 25 | 26 | var dict = new Dictionary(); 27 | 28 | var aggregater = new ActionBlock>(pair => 29 | { 30 | int oldValue; 31 | dict[pair.Key] = dict.TryGetValue(pair.Key, out oldValue) ? oldValue + pair.Value : pair.Value; 32 | }); 33 | 34 | var dataflow = _factory.FromPropagator(splitter) 35 | .LinkToTarget(aggregater) 36 | .Create(); 37 | 38 | result = dict; 39 | return dataflow; 40 | } 41 | 42 | private static void TestAggregatorFlow() 43 | { 44 | var dataflow = GetAggregatorFlow(out Dictionary result); 45 | dataflow.Post("a=1"); 46 | dataflow.Post("b=2"); 47 | dataflow.Post("a=5"); 48 | dataflow.Complete(); 49 | 50 | Task.WaitAll(dataflow.Completion); 51 | 52 | System.Console.WriteLine("sum(a) = {0}", result["a"]); //prints sum(a) = 6 53 | } 54 | 55 | private static ITargetBlock GetLineAggregatorFlow(out Dictionary result) 56 | { 57 | var aggregator = GetAggregatorFlow(out result); 58 | var splitter = new TransformManyBlock(line => line.Split(' ')); 59 | var dataflow = _factory.FromPropagator(splitter) 60 | .LinkToTarget(aggregator) 61 | .Create(); 62 | 63 | return dataflow; 64 | } 65 | 66 | private static void TestLineAggregatorFlow() 67 | { 68 | var dataflow = GetLineAggregatorFlow(out Dictionary result); 69 | dataflow.Post("a=1 b=2 a=5"); 70 | dataflow.Post("c=6 b=8"); 71 | dataflow.Complete(); 72 | 73 | Task.WaitAll(dataflow.Completion); 74 | 75 | System.Console.WriteLine("sum(b) = {0}", result["b"]); //prints sum(b) = 10 76 | } 77 | 78 | private static void TestAggregatorFlowOnError() 79 | { 80 | var dataflow = GetAggregatorFlow(out Dictionary result); 81 | dataflow.Post("a=1"); 82 | dataflow.Post("b=2"); 83 | dataflow.Post("a=badstring"); 84 | dataflow.Complete(); 85 | 86 | Task.WaitAll(dataflow.Completion); 87 | } 88 | 89 | private static ITargetBlock GetBroadcastFlow() 90 | { 91 | var printer1 = new ActionBlock(s => System.Console.WriteLine("Printer1: {0}", s)); 92 | var printer2 = new ActionBlock(s => System.Console.WriteLine("Printer2: {0}", s)); 93 | var printer3 = new ActionBlock(s => System.Console.WriteLine("Printer3: {0}", s)); 94 | 95 | var dataflow = _factory.FromBroadcast() 96 | .LinkTo(printer1) 97 | .LinkTo(printer2) 98 | .LinkTo(printer3) 99 | .Create(); 100 | 101 | return dataflow; 102 | } 103 | 104 | private static void TestBroadcastFlow() 105 | { 106 | var dataflow = GetBroadcastFlow(); 107 | dataflow.Post("first message"); 108 | dataflow.Post("second message"); 109 | dataflow.Post("third message"); 110 | dataflow.Complete(); 111 | 112 | Task.WaitAll(dataflow.Completion); 113 | } 114 | 115 | private static void TestMultipleSourcesFlow() 116 | { 117 | // by default, with native TPL, if multiple sources link to the same target, 118 | // if set PropagateCompletion=true, 119 | // as long as one of the source complets, the target complets. 120 | // the target will miss some of the messages from the other sources. 121 | 122 | // BUT, with our dataflow factory here, when set PropagateCompletion=true, 123 | // dataflow.Complete() internally waits for all the sources to complete, 124 | // so the target is guaranteed to receive all the messages from all the sources 125 | 126 | var source1 = new BufferBlock(); 127 | var source2 = new BufferBlock(); 128 | var source3 = new BufferBlock(); 129 | var printer = new ActionBlock(s => System.Console.WriteLine(s)); 130 | 131 | var dataflow = _factory.FromMultipleSources(source1, source2, source3) 132 | .LinkToTarget(printer) 133 | .Create(); 134 | 135 | for (var i = 0; i < 3; ++i) 136 | { 137 | var s = i.ToString(); 138 | source1.Post(s); 139 | source2.Post(s); 140 | source3.Post(s); 141 | } 142 | 143 | dataflow.Complete(); 144 | 145 | Task.WaitAll(dataflow.Completion); 146 | } 147 | 148 | private static void TestDataflowLinkWithFilter() 149 | { 150 | // the filter only accepts even numbers, 151 | // so odd numbers goes to declined printer 152 | var filter = new Predicate(i => 153 | { 154 | return i % 2 == 0; 155 | }); 156 | var printer = new ActionBlock(s => System.Console.WriteLine("printer: " + s.ToString())); 157 | var declinedPrinter = new ActionBlock(s => System.Console.WriteLine("declined: " + s.ToString())); 158 | var inputBlock = new BufferBlock(); 159 | 160 | var dataflow = _factory.FromPropagator(inputBlock) 161 | .LinkToTarget(printer 162 | , filter 163 | // when linking with filter, you have to specify a declined block 164 | // otherwise, because there will be messages declined still in the queue, 165 | // the current block will not be able to COMPLETE (waits on its Completion will never return) 166 | , declinedPrinter) 167 | .Create(); 168 | 169 | for (int i = 0; i < 10; ++i) 170 | { 171 | dataflow.Post(i); 172 | } 173 | 174 | dataflow.Complete(); 175 | 176 | Task.WaitAll(dataflow.Completion); 177 | } 178 | 179 | private static void TestBatch() 180 | { 181 | var source = new BufferBlock(); 182 | var printer = new ActionBlock>(s => System.Console.WriteLine("printer: " +string.Join("|", s))); 183 | 184 | var dataflow = _factory.FromSource(source) 185 | .Batch(2) 186 | .LinkToTarget(printer) 187 | .Create(); 188 | 189 | for (var i = 0; i < 6; ++i) 190 | { 191 | var s = i.ToString(); 192 | source.Post(s); 193 | } 194 | 195 | dataflow.Complete(); 196 | 197 | Task.WaitAll(dataflow.Completion); 198 | } 199 | 200 | private static void TestJoin() 201 | { 202 | var source1 = new BufferBlock(); 203 | var source2 = new BufferBlock(); 204 | var printer = new ActionBlock>(s => System.Console.WriteLine("printer: {0},{1}", s.Item1, s.Item2)); 205 | 206 | var dataflow = _factory.Join(source1, source2) 207 | .LinkToTarget(printer) 208 | .Create(); 209 | 210 | for (var i = 0; i < 3; ++i) 211 | { 212 | var s = i.ToString(); 213 | source1.Post(s); 214 | source2.Post(s); 215 | } 216 | 217 | dataflow.Complete(); 218 | 219 | Task.WaitAll(dataflow.Completion); 220 | } 221 | 222 | private static void TestBatchedJoin() 223 | { 224 | var source1 = new BufferBlock(); 225 | var source2 = new BufferBlock(); 226 | var printer = new ActionBlock, IList>>(s => System.Console.WriteLine("printer: {0},{1}", string.Join("|", s.Item1), string.Join("|", s.Item2))); 227 | 228 | var dataflow = _factory.BatchedJoin(source1, source2, 2) 229 | .LinkToTarget(printer) 230 | .Create(); 231 | 232 | for (var i = 0; i < 4; ++i) 233 | { 234 | var s = i.ToString(); 235 | source1.Post(s); 236 | source2.Post(s); 237 | } 238 | 239 | dataflow.Complete(); 240 | 241 | Task.WaitAll(dataflow.Completion); 242 | } 243 | 244 | private static void TestIfElseBranchingAndMerging() 245 | { 246 | // the ifFilter only accepts even numbers, 247 | // so odd numbers goes to elseBlock 248 | var ifFilter = new Predicate(i => 249 | { 250 | return i % 2 == 0; 251 | }); 252 | var printer = new ActionBlock(s => System.Console.WriteLine("printer: " + s.ToString())); 253 | var inputBlock = new BufferBlock(); 254 | // if meet ifFilter, convert to: i -> i * 10 255 | var ifBlock = new TransformBlock(i => i * 10); 256 | // else, convert to: i -> i * 100 257 | var elseBlock = new TransformBlock(i => i * 100); 258 | 259 | var branchingDataflow = _factory.FromPropagator(inputBlock) 260 | .LinkToPropagator(ifBlock, ifFilter, elseBlock) 261 | .Create(); 262 | 263 | var mergeingDataflow = _factory.FromMultipleSources(ifBlock, elseBlock) 264 | .LinkToTarget(printer) 265 | .Create(); 266 | 267 | //encapsulate branchingDataflow and mergeingDataflow 268 | var dataflow = _factory.EncapsulateTargetDataflow(branchingDataflow, mergeingDataflow); 269 | 270 | for (int i = 0; i < 10; ++i) 271 | { 272 | dataflow.Post(i); 273 | } 274 | 275 | dataflow.Complete(); 276 | 277 | Task.WaitAll(dataflow.Completion); 278 | } 279 | 280 | private static void TestFluentOptions() 281 | { 282 | var joinOptions = new DataflowJoinOptions( 283 | join => join.BoundedCapacity(11).EnsureOrdered().MaxNumberOfGroups(1) 284 | , target2: _ => _.Append().MaxMessages(1).PropagateCompletion()); 285 | 286 | var batchOptions = new DataflowBatchOptions( 287 | batch => batch.BoundedCapacity(2).Greedy().MaxNumberOfGroups(3) 288 | , link => link.MaxMessages(1).PropagateCompletion(false) 289 | ); 290 | 291 | var linkOptions = new DataflowLinkOptions().PropagateCompletion(false).MaxMessages(2); 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.Console/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FluentDataflow.Tests.Console")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FluentDataflow.Tests.Console")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("1b97e995-3fcf-4356-bcea-91f254d6fa66")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.Console/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/DataflowWrapperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Moq; 4 | using System.Threading.Tasks.Dataflow; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentDataflow.Tests.UnitTests 8 | { 9 | [TestClass] 10 | public class DataflowWrapperTests 11 | { 12 | [TestMethod] 13 | public async Task TestDataflowWrapper() 14 | { 15 | var mockOriginalSourceBlock = new Mock(); 16 | var mockCurrentSourceBlock = new Mock(); 17 | var mockTargetBlock = new Mock(); 18 | 19 | var target = new DataflowWrapper(mockOriginalSourceBlock.Object, mockCurrentSourceBlock.Object, mockTargetBlock.Object, true); 20 | 21 | // test target.Complete() 22 | bool originalCompleteCalled = false; 23 | mockOriginalSourceBlock.Setup(b => b.Complete()).Callback(() => originalCompleteCalled = true); 24 | target.Complete(); 25 | Assert.IsTrue(originalCompleteCalled); 26 | 27 | // test target.Fault() 28 | bool originalFaultCalled = false; 29 | mockOriginalSourceBlock.Setup(b => b.Fault(It.IsAny())).Callback(ex => 30 | { 31 | originalFaultCalled = true; 32 | 33 | Assert.IsNotNull(ex); 34 | }); 35 | target.Fault(new Exception()); 36 | Assert.IsTrue(originalFaultCalled); 37 | 38 | // test target.Completion without error 39 | var task = Task.FromResult(0); 40 | mockCurrentSourceBlock.Setup(b => b.Completion).Returns(task); 41 | bool targetCompleteCalled = false; 42 | mockTargetBlock.Setup(b => b.Complete()).Callback(() => targetCompleteCalled = true); 43 | var task2 = Task.FromResult(222); 44 | mockTargetBlock.Setup(b => b.Completion).Returns(task2); 45 | var resultTask = target.Completion; 46 | await resultTask; 47 | Assert.IsTrue(targetCompleteCalled); 48 | Assert.AreEqual(222, ((resultTask as Task).Result as Task).Result); 49 | 50 | // test target.Completion with error 51 | bool targetFalutCalled = false; 52 | mockTargetBlock.Setup(b => b.Fault(It.IsAny())).Callback(ex => 53 | { 54 | targetFalutCalled = true; 55 | Assert.IsNotNull(ex); 56 | }); 57 | var task3 = Task.FromException(new Exception()); 58 | mockCurrentSourceBlock.Setup(b => b.Completion).Returns(task3); 59 | await target.Completion; 60 | Assert.IsTrue(targetFalutCalled); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/FluentDataflow.Tests.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2247882D-45A4-4ADE-84AF-414C46DC394D} 8 | Library 9 | Properties 10 | FluentDataflow.Tests.UnitTests 11 | FluentDataflow.Tests.UnitTests 12 | v4.6.1 13 | 512 14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 15.0 16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 18 | False 19 | UnitTest 20 | 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | ..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll 43 | 44 | 45 | ..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll 46 | 47 | 48 | ..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll 49 | 50 | 51 | ..\packages\Moq.4.7.142\lib\net45\Moq.dll 52 | 53 | 54 | 55 | 56 | 57 | ..\packages\System.Threading.Tasks.Dataflow.4.8.0\lib\netstandard2.0\System.Threading.Tasks.Dataflow.dll 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {d339ef89-bfcf-47f1-b618-a8fcd382649b} 77 | FluentDataflow 78 | 79 | 80 | 81 | 82 | 83 | 84 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/MultipleDataflowWrapperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.Threading.Tasks.Dataflow; 4 | using Moq; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentDataflow.Tests.UnitTests 8 | { 9 | [TestClass] 10 | public class MultipleDataflowWrapperTests 11 | { 12 | [TestMethod] 13 | public async Task TestMultipleDataflowWrapper() 14 | { 15 | var mockSourceBlock = new Mock(); 16 | 17 | var target = new MultipleSourceDataflowWrapper(new[] { mockSourceBlock.Object }); 18 | 19 | // test target.Complete() 20 | bool sourceCompleteCalled = false; 21 | mockSourceBlock.Setup(b => b.Complete()).Callback(() => sourceCompleteCalled = true); 22 | target.Complete(); 23 | Assert.IsTrue(sourceCompleteCalled); 24 | 25 | // test target.Fault() 26 | bool sourceFaultCalled = false; 27 | mockSourceBlock.Setup(b => b.Fault(It.IsAny())).Callback(ex => 28 | { 29 | sourceFaultCalled = true; 30 | 31 | Assert.IsNotNull(ex); 32 | }); 33 | target.Fault(new Exception()); 34 | Assert.IsTrue(sourceFaultCalled); 35 | 36 | // test target.Completion without error 37 | var task = Task.FromResult(222); 38 | mockSourceBlock.Setup(b => b.Completion).Returns(task); 39 | var resultTask = target.Completion; 40 | await resultTask; 41 | Assert.AreEqual(TaskStatus.RanToCompletion, resultTask.Status); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/MultipleSourceDataflowBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Moq; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow.Tests.UnitTests 6 | { 7 | [TestClass] 8 | public class MultipleSourceDataflowBuilderTests 9 | { 10 | [TestMethod] 11 | public void TestMultipleSourceDataflowBuilder() 12 | { 13 | var mockSourceBlock = new Mock>(); 14 | 15 | var target = new MultipleSourceDataflowBuilder(new[] { mockSourceBlock.Object }); 16 | 17 | // test target.LinkToTarget 18 | bool finalSourceLinkToCalled = false; 19 | var mockTargetBlock = new Mock>(); 20 | mockSourceBlock.Setup(b => b.LinkTo(It.IsAny>(), It.IsAny())).Callback(() => finalSourceLinkToCalled = true); 21 | var builder1 = target.LinkToTarget(mockTargetBlock.Object, null, null) as DataflowBuilder; 22 | Assert.IsTrue(finalSourceLinkToCalled); 23 | Assert.IsNotNull(builder1); 24 | Assert.IsInstanceOfType(builder1.OriginalSourceBlock, typeof(MultipleSourceDataflowWrapper)); 25 | Assert.IsInstanceOfType(builder1.CurrentSourceBlock, typeof(MultipleSourceDataflowWrapper)); 26 | Assert.AreEqual(mockTargetBlock.Object, builder1.TargetBlock); 27 | Assert.IsTrue(builder1.PropagateCompletion.GetValueOrDefault()); 28 | 29 | // test target.LinkToPropagator 30 | finalSourceLinkToCalled = false; 31 | var mockPropagatorBlock = new Mock>(); 32 | var builder2 = target.LinkToPropagator(mockPropagatorBlock.Object, null, null) as SourceDataflowBuilder; 33 | Assert.IsTrue(finalSourceLinkToCalled); 34 | Assert.IsNotNull(builder2); 35 | Assert.IsInstanceOfType(builder2.OriginalSourceBlock, typeof(MultipleSourceDataflowWrapper)); 36 | Assert.IsInstanceOfType(builder2.CurrentSourceBlock, typeof(MultipleSourceDataflowWrapper)); 37 | Assert.AreEqual(mockPropagatorBlock.Object, builder2.FinalSourceBlock); 38 | Assert.IsTrue(builder2.PropagateCompletion.GetValueOrDefault()); 39 | 40 | // test target.Batch 41 | finalSourceLinkToCalled = false; 42 | var builder3 = target.Batch(2, default(DataflowBatchOptions)) as SourceDataflowBuilder; 43 | Assert.IsTrue(finalSourceLinkToCalled); 44 | Assert.IsNotNull(builder3); 45 | Assert.IsInstanceOfType(builder3.OriginalSourceBlock, typeof(MultipleSourceDataflowWrapper)); 46 | Assert.IsInstanceOfType(builder3.CurrentSourceBlock, typeof(MultipleSourceDataflowWrapper)); 47 | Assert.IsInstanceOfType(builder3.FinalSourceBlock, typeof(BatchBlock)); 48 | Assert.IsTrue(builder3.PropagateCompletion.GetValueOrDefault()); 49 | 50 | // test target.WriteOnce 51 | finalSourceLinkToCalled = false; 52 | var builder4 = target.WriteOnce(i => i, default(DataflowWriteOnceOptions)) as SourceDataflowBuilder; 53 | Assert.IsTrue(finalSourceLinkToCalled); 54 | Assert.IsNotNull(builder4); 55 | Assert.IsInstanceOfType(builder4.OriginalSourceBlock, typeof(MultipleSourceDataflowWrapper)); 56 | Assert.IsInstanceOfType(builder4.CurrentSourceBlock, typeof(MultipleSourceDataflowWrapper)); 57 | Assert.IsInstanceOfType(builder4.FinalSourceBlock, typeof(WriteOnceBlock)); 58 | Assert.IsTrue(builder4.PropagateCompletion.GetValueOrDefault()); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/PropagatorDataflowBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Moq; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace FluentDataflow.Tests.UnitTests 7 | { 8 | [TestClass] 9 | public class PropagatorDataflowBuilderTests 10 | { 11 | [TestMethod] 12 | public void TestPropagatorDataflowBuilder() 13 | { 14 | var mockOriginalTargetBlock = new Mock>(); 15 | var mockCurrentSourceBlock = new Mock(); 16 | var mockFinalSourceBlock = new Mock>(); 17 | 18 | var target = new PropagatorDataflowBuilder(mockOriginalTargetBlock.Object, mockCurrentSourceBlock.Object, mockFinalSourceBlock.Object, true); 19 | 20 | // test target.LinkToTarget 21 | bool finalSourceLinkToCalled = false; 22 | var mockTargetBlock = new Mock>(); 23 | mockFinalSourceBlock.Setup(b => b.LinkTo(It.IsAny>(), It.IsAny())).Callback(() => finalSourceLinkToCalled = true); 24 | var builder1 = target.LinkToTarget(mockTargetBlock.Object, null, null) as TargetDataflowBuilder; 25 | Assert.IsTrue(finalSourceLinkToCalled); 26 | Assert.IsNotNull(builder1); 27 | Assert.AreEqual(mockOriginalTargetBlock.Object, builder1.OriginalTargetBlock); 28 | Assert.AreEqual(mockTargetBlock.Object, builder1.FinalTargetBlock); 29 | 30 | // test target.LinkToPropagator 31 | finalSourceLinkToCalled = false; 32 | var mockPropagatorBlock = new Mock>(); 33 | var builder2 = target.LinkToPropagator(mockPropagatorBlock.Object, null, null) as PropagatorDataflowBuilder; 34 | Assert.IsTrue(finalSourceLinkToCalled); 35 | Assert.IsNotNull(builder2); 36 | Assert.AreEqual(mockOriginalTargetBlock.Object, builder2.OriginalTargetBlock); 37 | Assert.AreEqual(mockFinalSourceBlock.Object, builder2.CurrentSourceBlock); 38 | Assert.AreEqual(mockPropagatorBlock.Object, builder2.FinalSourceBlock); 39 | Assert.IsTrue(builder2.PropagateCompletion.GetValueOrDefault()); 40 | 41 | // test target.Batch 42 | finalSourceLinkToCalled = false; 43 | var builder3 = target.Batch(2, default(DataflowBatchOptions)) as PropagatorDataflowBuilder; 44 | Assert.IsTrue(finalSourceLinkToCalled); 45 | Assert.IsNotNull(builder3); 46 | Assert.AreEqual(mockOriginalTargetBlock.Object, builder3.OriginalTargetBlock); 47 | Assert.AreEqual(mockFinalSourceBlock.Object, builder3.CurrentSourceBlock); 48 | Assert.IsInstanceOfType(builder3.FinalSourceBlock, typeof(BatchBlock)); 49 | Assert.IsTrue(builder3.PropagateCompletion.GetValueOrDefault()); 50 | 51 | // test target.WriteOnce 52 | finalSourceLinkToCalled = false; 53 | var builder4 = target.WriteOnce(i => i, default(DataflowWriteOnceOptions)) as PropagatorDataflowBuilder; 54 | Assert.IsTrue(finalSourceLinkToCalled); 55 | Assert.IsNotNull(builder4); 56 | Assert.AreEqual(mockOriginalTargetBlock.Object, builder4.OriginalTargetBlock); 57 | Assert.AreEqual(mockFinalSourceBlock.Object, builder4.CurrentSourceBlock); 58 | Assert.IsInstanceOfType(builder4.FinalSourceBlock, typeof(WriteOnceBlock)); 59 | Assert.IsTrue(builder4.PropagateCompletion.GetValueOrDefault()); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/PropagatorDataflowWrapperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | using Moq; 6 | 7 | namespace FluentDataflow.Tests.UnitTests 8 | { 9 | [TestClass] 10 | public class PropagatorDataflowWrapperTests 11 | { 12 | [TestMethod] 13 | public async Task TestPropagatorDataflowWrapper() 14 | { 15 | var mockOriginalTargetBlock = new Mock>(); 16 | var mockCurrentSourceBlock = new Mock(); 17 | var mockFinalSourceBlock = new Mock>(); 18 | 19 | var target = new PropagatorDataflowWrapper(mockOriginalTargetBlock.Object, mockCurrentSourceBlock.Object, mockFinalSourceBlock.Object, true); 20 | 21 | // test target.Complete() 22 | bool originalCompleteCalled = false; 23 | mockOriginalTargetBlock.Setup(b => b.Complete()).Callback(() => originalCompleteCalled = true); 24 | target.Complete(); 25 | Assert.IsTrue(originalCompleteCalled); 26 | 27 | // test target.Fault() 28 | bool originalFaultCalled = false; 29 | mockOriginalTargetBlock.Setup(b => b.Fault(It.IsAny())).Callback(ex => 30 | { 31 | originalFaultCalled = true; 32 | 33 | Assert.IsNotNull(ex); 34 | }); 35 | target.Fault(new Exception()); 36 | Assert.IsTrue(originalFaultCalled); 37 | 38 | // test target.Completion without error 39 | var task = Task.FromResult(0); 40 | mockCurrentSourceBlock.Setup(b => b.Completion).Returns(task); 41 | bool finalSourceCompleteCalled = false; 42 | mockFinalSourceBlock.Setup(b => b.Complete()).Callback(() => finalSourceCompleteCalled = true); 43 | var task2 = Task.FromResult(222); 44 | mockFinalSourceBlock.Setup(b => b.Completion).Returns(task2); 45 | var resultTask = target.Completion; 46 | await resultTask; 47 | Assert.IsTrue(finalSourceCompleteCalled); 48 | Assert.AreEqual(222, ((resultTask as Task).Result as Task).Result); 49 | 50 | // test target.Completion with error 51 | bool finalSourceFalutCalled = false; 52 | mockFinalSourceBlock.Setup(b => b.Fault(It.IsAny())).Callback(ex => 53 | { 54 | finalSourceFalutCalled = true; 55 | Assert.IsNotNull(ex); 56 | }); 57 | var task3 = Task.FromException(new Exception()); 58 | mockCurrentSourceBlock.Setup(b => b.Completion).Returns(task3); 59 | await target.Completion; 60 | Assert.IsTrue(finalSourceFalutCalled); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("FluentDataflow.Tests.UnitTests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("FluentDataflow.Tests.UnitTests")] 10 | [assembly: AssemblyCopyright("Copyright © 2017")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("2247882d-45a4-4ade-84af-414c46dc394d")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/SourceDataflowBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Moq; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow.Tests.UnitTests 6 | { 7 | [TestClass] 8 | public class SourceDataflowBuilderTests 9 | { 10 | [TestMethod] 11 | public void TestSourceDataflowBuilder() 12 | { 13 | var mockOriginalSourceBlock = new Mock(); 14 | var mockCurrentSourceBlock = new Mock(); 15 | var mockFinalSourceBlock = new Mock>(); 16 | 17 | var target = new SourceDataflowBuilder(mockOriginalSourceBlock.Object, mockCurrentSourceBlock.Object, mockFinalSourceBlock.Object, true); 18 | 19 | // test target.LinkToTarget 20 | bool finalSourceLinkToCalled = false; 21 | var mockTargetBlock = new Mock>(); 22 | mockFinalSourceBlock.Setup(b => b.LinkTo(It.IsAny>(), It.IsAny())).Callback(() => finalSourceLinkToCalled = true); 23 | var builder1 = target.LinkToTarget(mockTargetBlock.Object, null, null) as DataflowBuilder; 24 | Assert.IsTrue(finalSourceLinkToCalled); 25 | Assert.IsNotNull(builder1); 26 | Assert.AreEqual(mockOriginalSourceBlock.Object, builder1.OriginalSourceBlock); 27 | Assert.AreEqual(mockFinalSourceBlock.Object, builder1.CurrentSourceBlock); 28 | Assert.AreEqual(mockTargetBlock.Object, builder1.TargetBlock); 29 | Assert.IsTrue(builder1.PropagateCompletion.GetValueOrDefault()); 30 | 31 | // test target.LinkToPropagator 32 | finalSourceLinkToCalled = false; 33 | var mockPropagatorBlock = new Mock>(); 34 | var builder2 = target.LinkToPropagator(mockPropagatorBlock.Object, null, null) as SourceDataflowBuilder; 35 | Assert.IsTrue(finalSourceLinkToCalled); 36 | Assert.IsNotNull(builder2); 37 | Assert.AreEqual(mockOriginalSourceBlock.Object, builder2.OriginalSourceBlock); 38 | Assert.AreEqual(mockFinalSourceBlock.Object, builder2.CurrentSourceBlock); 39 | Assert.AreEqual(mockPropagatorBlock.Object, builder2.FinalSourceBlock); 40 | Assert.IsTrue(builder2.PropagateCompletion.GetValueOrDefault()); 41 | 42 | // test target.Batch 43 | finalSourceLinkToCalled = false; 44 | var builder3 = target.Batch(2, default(DataflowBatchOptions)) as SourceDataflowBuilder; 45 | Assert.IsTrue(finalSourceLinkToCalled); 46 | Assert.IsNotNull(builder3); 47 | Assert.AreEqual(mockOriginalSourceBlock.Object, builder3.OriginalSourceBlock); 48 | Assert.AreEqual(mockFinalSourceBlock.Object, builder3.CurrentSourceBlock); 49 | Assert.IsInstanceOfType(builder3.FinalSourceBlock, typeof(BatchBlock)); 50 | Assert.IsTrue(builder3.PropagateCompletion.GetValueOrDefault()); 51 | 52 | // test target.WriteOnce 53 | finalSourceLinkToCalled = false; 54 | var builder4 = target.WriteOnce(i => i, default(DataflowWriteOnceOptions)) as SourceDataflowBuilder; 55 | Assert.IsTrue(finalSourceLinkToCalled); 56 | Assert.IsNotNull(builder4); 57 | Assert.AreEqual(mockOriginalSourceBlock.Object, builder4.OriginalSourceBlock); 58 | Assert.AreEqual(mockFinalSourceBlock.Object, builder4.CurrentSourceBlock); 59 | Assert.IsInstanceOfType(builder4.FinalSourceBlock, typeof(WriteOnceBlock)); 60 | Assert.IsTrue(builder4.PropagateCompletion.GetValueOrDefault()); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/SourceDataflowWrapperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Moq; 4 | using System.Threading.Tasks.Dataflow; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentDataflow.Tests.UnitTests 8 | { 9 | [TestClass] 10 | public class SourceDataflowWrapperTests 11 | { 12 | [TestMethod] 13 | public async Task TestSourceDataflowWrapper() 14 | { 15 | var mockOriginalSourceBlock = new Mock(); 16 | var mockCurrentSourceBlock = new Mock(); 17 | var mockFinalSourceBlock = new Mock>(); 18 | 19 | var target = new SourceDataflowWrapper(mockOriginalSourceBlock.Object, mockCurrentSourceBlock.Object, mockFinalSourceBlock.Object, true); 20 | 21 | // test target.Complete() 22 | bool originalCompleteCalled = false; 23 | mockOriginalSourceBlock.Setup(b => b.Complete()).Callback(() => originalCompleteCalled = true); 24 | target.Complete(); 25 | Assert.IsTrue(originalCompleteCalled); 26 | 27 | // test target.Fault() 28 | bool originalFaultCalled = false; 29 | mockOriginalSourceBlock.Setup(b => b.Fault(It.IsAny())).Callback(ex => 30 | { 31 | originalFaultCalled = true; 32 | 33 | Assert.IsNotNull(ex); 34 | }); 35 | target.Fault(new Exception()); 36 | Assert.IsTrue(originalFaultCalled); 37 | 38 | // test target.Completion without error 39 | var task = Task.FromResult(0); 40 | mockCurrentSourceBlock.Setup(b => b.Completion).Returns(task); 41 | bool finalSourceCompleteCalled = false; 42 | mockFinalSourceBlock.Setup(b => b.Complete()).Callback(() => finalSourceCompleteCalled = true); 43 | var task2 = Task.FromResult(222); 44 | mockFinalSourceBlock.Setup(b => b.Completion).Returns(task2); 45 | var resultTask = target.Completion; 46 | await resultTask; 47 | Assert.IsTrue(finalSourceCompleteCalled); 48 | Assert.AreEqual(222, ((resultTask as Task).Result as Task).Result); 49 | 50 | // test target.Completion with error 51 | bool finalSourceFalutCalled = false; 52 | mockFinalSourceBlock.Setup(b => b.Fault(It.IsAny())).Callback(ex => 53 | { 54 | finalSourceFalutCalled = true; 55 | Assert.IsNotNull(ex); 56 | }); 57 | var task3 = Task.FromException(new Exception()); 58 | mockCurrentSourceBlock.Setup(b => b.Completion).Returns(task3); 59 | await target.Completion; 60 | Assert.IsTrue(finalSourceFalutCalled); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/TargetDataflowWrapperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | using Moq; 6 | 7 | namespace FluentDataflow.Tests.UnitTests 8 | { 9 | [TestClass] 10 | public class TargetDataflowWrapperTests 11 | { 12 | [TestMethod] 13 | public void TestTargetDataflowWrapper() 14 | { 15 | var mockOriginalTargetBlock = new Mock>(); 16 | var mockFinalTargetBlock = new Mock(); 17 | 18 | var target = new TargetDataflowWrapper(mockOriginalTargetBlock.Object, mockFinalTargetBlock.Object); 19 | 20 | // test target.Complete() 21 | bool originalCompleteCalled = false; 22 | mockOriginalTargetBlock.Setup(b => b.Complete()).Callback(() => originalCompleteCalled = true); 23 | target.Complete(); 24 | Assert.IsTrue(originalCompleteCalled); 25 | 26 | // test target.Fault() 27 | bool originalFaultCalled = false; 28 | mockOriginalTargetBlock.Setup(b => b.Fault(It.IsAny())).Callback(ex => 29 | { 30 | originalFaultCalled = true; 31 | 32 | Assert.IsNotNull(ex); 33 | }); 34 | target.Fault(new Exception()); 35 | Assert.IsTrue(originalFaultCalled); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /FluentDataflow.Tests.UnitTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FluentDataflow.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentDataflow", "FluentDataflow\FluentDataflow.csproj", "{D339EF89-BFCF-47F1-B618-A8FCD382649B}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentDataflow.Tests.Console", "FluentDataflow.Tests.Console\FluentDataflow.Tests.Console.csproj", "{1B97E995-3FCF-4356-BCEA-91F254D6FA66}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentDataflow.Tests.UnitTests", "FluentDataflow.Tests.UnitTests\FluentDataflow.Tests.UnitTests.csproj", "{2247882D-45A4-4ADE-84AF-414C46DC394D}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BFCDCA69-55BB-4224-9E90-9674E4439544}" 13 | ProjectSection(SolutionItems) = preProject 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentDataflowPortable", "FluentDataflowPortable\FluentDataflowPortable.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}" 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Release|Any CPU = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {D339EF89-BFCF-47F1-B618-A8FCD382649B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {D339EF89-BFCF-47F1-B618-A8FCD382649B}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {D339EF89-BFCF-47F1-B618-A8FCD382649B}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {D339EF89-BFCF-47F1-B618-A8FCD382649B}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {1B97E995-3FCF-4356-BCEA-91F254D6FA66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {1B97E995-3FCF-4356-BCEA-91F254D6FA66}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {1B97E995-3FCF-4356-BCEA-91F254D6FA66}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {1B97E995-3FCF-4356-BCEA-91F254D6FA66}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {2247882D-45A4-4ADE-84AF-414C46DC394D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {2247882D-45A4-4ADE-84AF-414C46DC394D}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {2247882D-45A4-4ADE-84AF-414C46DC394D}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {2247882D-45A4-4ADE-84AF-414C46DC394D}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|Any CPU.Build.0 = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(SolutionProperties) = preSolution 43 | HideSolutionNode = FALSE 44 | EndGlobalSection 45 | GlobalSection(ExtensibilityGlobals) = postSolution 46 | SolutionGuid = {112A5B40-8933-4453-A2C9-9BE90A0BB048} 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /FluentDataflow/Assembly.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("FluentDataflow.Tests.UnitTests")] 4 | -------------------------------------------------------------------------------- /FluentDataflow/BroadcastDataflowBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace FluentDataflow 7 | { 8 | internal class BroadcastDataflowBuilder : IBroadcastDataflowBuilder 9 | { 10 | private readonly BroadcastBlock _broadcastBlock; 11 | private readonly IList> _targetBlocks = new List>(); 12 | 13 | public BroadcastBlock BroadcastBock => _broadcastBlock; 14 | 15 | public BroadcastDataflowBuilder(BroadcastBlock broadcastBlock, ITargetBlock targetBlock = null) 16 | { 17 | _broadcastBlock = broadcastBlock; 18 | if (targetBlock != null && !_targetBlocks.Contains(targetBlock)) 19 | { 20 | _targetBlocks.Add(targetBlock); 21 | } 22 | } 23 | 24 | public ITargetBlock Create() 25 | { 26 | return new BroadcastDataflowWrapper(_broadcastBlock, _targetBlocks.ToArray()); 27 | } 28 | 29 | public IBroadcastDataflowBuilder LinkTo(ITargetBlock targetBlock, DataflowLinkOptions linkOptions = null, Predicate predicate = null) 30 | { 31 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 32 | 33 | LinkHelper.Link(_broadcastBlock, targetBlock, linkOptions, predicate); 34 | return new BroadcastDataflowBuilder(_broadcastBlock, targetBlock); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /FluentDataflow/BroadcastDataflowWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace FluentDataflow 7 | { 8 | internal class BroadcastDataflowWrapper : ITargetBlock 9 | { 10 | private readonly ITargetBlock _broadcastBlock; 11 | private readonly ITargetBlock[] _targetBlocks; 12 | 13 | public BroadcastDataflowWrapper(BroadcastBlock broadcastBlock, ITargetBlock[] targetBlocks) 14 | { 15 | _broadcastBlock = broadcastBlock; 16 | _targetBlocks = targetBlocks; 17 | } 18 | 19 | public Task Completion 20 | { 21 | get 22 | { 23 | if (_targetBlocks == null || _targetBlocks.Length == 0) return _broadcastBlock.Completion; 24 | 25 | return _broadcastBlock.Completion.ContinueWith(task => 26 | { 27 | if (task.IsFaulted) 28 | { 29 | foreach (var targetBlock in _targetBlocks) 30 | { 31 | targetBlock.Fault(task.Exception); 32 | } 33 | } 34 | else 35 | { 36 | foreach (var targetBlock in _targetBlocks) 37 | { 38 | targetBlock.Complete(); 39 | } 40 | } 41 | 42 | return Task.WhenAll(_targetBlocks.Select(b => b.Completion)); 43 | }); 44 | } 45 | } 46 | 47 | public void Complete() 48 | { 49 | _broadcastBlock.Complete(); 50 | } 51 | 52 | public void Fault(Exception exception) 53 | { 54 | _broadcastBlock.Fault(exception); 55 | } 56 | 57 | public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) 58 | { 59 | return _broadcastBlock.OfferMessage(messageHeader, messageValue, source, consumeToAccept); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowBatchOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks.Dataflow; 3 | 4 | namespace FluentDataflow 5 | { 6 | /// 7 | /// Options for applying batching. 8 | /// 9 | public struct DataflowBatchOptions 10 | { 11 | private GroupingDataflowBlockOptions _batchOptions; 12 | private DataflowLinkOptions _linkOptions; 13 | 14 | /// 15 | /// Initializes a DataflowBatchOptions. 16 | /// 17 | /// 18 | /// 19 | public DataflowBatchOptions(Action batch = null 20 | , Action link = null) 21 | { 22 | _batchOptions = DataflowDefaultOptions.DefaultGroupingBlockOptions; 23 | _linkOptions = DataflowDefaultOptions.DefaultLinkOptions; 24 | 25 | if (batch != null) batch(_batchOptions); 26 | if (link != null) link(_linkOptions); 27 | } 28 | 29 | /// 30 | /// Block options for creating batch block. 31 | /// 32 | public GroupingDataflowBlockOptions BatchBlockOptions 33 | { 34 | get 35 | { 36 | return _batchOptions ?? (_batchOptions = DataflowDefaultOptions.DefaultGroupingBlockOptions); 37 | } 38 | set 39 | { 40 | _batchOptions = value; 41 | } 42 | } 43 | 44 | /// 45 | /// Link options for linking to the batch block. 46 | /// 47 | public DataflowLinkOptions LinkOptions 48 | { 49 | get 50 | { 51 | return _linkOptions ?? (_linkOptions = DataflowDefaultOptions.DefaultLinkOptions); 52 | } 53 | set 54 | { 55 | _linkOptions = value; 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowBuilder.cs: -------------------------------------------------------------------------------- 1 | using FluentDataflow; 2 | using System.Threading.Tasks.Dataflow; 3 | 4 | namespace FluentDataflow 5 | { 6 | internal class DataflowBuilder : IDataflowBuilder 7 | { 8 | private readonly IDataflowBlock _originalSourceBlock; 9 | private readonly IDataflowBlock _currentSourceBlock; 10 | private readonly IDataflowBlock _targetBlock; 11 | private readonly bool? _propagateCompletion; 12 | 13 | public IDataflowBlock OriginalSourceBlock => _originalSourceBlock; 14 | public IDataflowBlock CurrentSourceBlock => _currentSourceBlock; 15 | public IDataflowBlock TargetBlock => _targetBlock; 16 | public bool? PropagateCompletion => _propagateCompletion; 17 | 18 | public DataflowBuilder(IDataflowBlock sourceBlock, IDataflowBlock targetBlock, bool? propagateCompletion = null) 19 | : this(sourceBlock, sourceBlock, targetBlock, propagateCompletion) 20 | { 21 | 22 | } 23 | 24 | public DataflowBuilder(IDataflowBlock orginalSourceBlock, IDataflowBlock currentSourceBlock, IDataflowBlock targetBlock, bool? propagateCompletion = null) 25 | { 26 | _originalSourceBlock = orginalSourceBlock; 27 | _currentSourceBlock = currentSourceBlock; 28 | _targetBlock = targetBlock; 29 | _propagateCompletion = propagateCompletion; 30 | } 31 | 32 | public IDataflowBlock Create() 33 | { 34 | return new DataflowWrapper(_originalSourceBlock, _currentSourceBlock, _targetBlock, _propagateCompletion); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowBuilderExtenions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace FluentDataflow 7 | { 8 | /// 9 | /// Extension methods for dataflow builders. 10 | /// 11 | public static class DataflowBuilderExtenions 12 | { 13 | #region IPropagatorDataflowBuilder link to ITargetBlock 14 | 15 | /// 16 | /// Links to target block. 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | public static ITargetDataflowBuilder LinkToTarget( 28 | this IPropagatorDataflowBuilder builder 29 | , ITargetBlock targetBlock 30 | , DataflowLinkOptions linkOptions 31 | , Predicate predicate 32 | , ITargetBlock declinedTargetBlock 33 | , DataflowLinkOptions declinedLinkOptions) 34 | { 35 | if (builder == null) return null; 36 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 37 | if (linkOptions == null) throw new ArgumentNullException("linkOptions"); 38 | if (predicate == null) throw new ArgumentNullException("predicate"); 39 | if (declinedTargetBlock == null) throw new ArgumentNullException("declinedTargetBlock"); 40 | if (declinedLinkOptions == null) throw new ArgumentNullException("declinedLinkOptions"); 41 | 42 | var nextBuilder = builder.LinkToTarget(targetBlock, linkOptions, predicate); 43 | 44 | if (declinedTargetBlock != null) 45 | { 46 | // LinkTo declined target 47 | LinkHelper.Link((builder as PropagatorDataflowBuilder).FinalSourceBlock, declinedTargetBlock, declinedLinkOptions ?? DataflowDefaultOptions.DefaultLinkOptions); 48 | } 49 | 50 | return nextBuilder; 51 | } 52 | 53 | /// 54 | /// Links to target block. 55 | /// 56 | /// 57 | /// 58 | /// 59 | /// 60 | /// 61 | /// 62 | /// 63 | public static ITargetDataflowBuilder LinkToTarget( 64 | this IPropagatorDataflowBuilder builder 65 | , ITargetBlock targetBlock 66 | , Predicate predicate 67 | , ITargetBlock declinedTargetBlock = null) 68 | { 69 | return builder.LinkToTarget(targetBlock, DataflowDefaultOptions.DefaultLinkOptions, predicate, declinedTargetBlock, DataflowDefaultOptions.DefaultLinkOptions); 70 | } 71 | 72 | #endregion 73 | 74 | #region IPropagatorDataflowBuilder link to IPropagatorBlock 75 | 76 | /// 77 | /// Links to a propagator block. 78 | /// 79 | /// 80 | /// 81 | /// 82 | /// 83 | /// 84 | /// 85 | /// 86 | /// 87 | /// 88 | /// 89 | public static IPropagatorDataflowBuilder LinkToPropagator( 90 | this IPropagatorDataflowBuilder builder 91 | , IPropagatorBlock propagatorBlock 92 | , DataflowLinkOptions linkOptions 93 | , Predicate predicate 94 | , ITargetBlock declinedTargetBlock 95 | , DataflowLinkOptions declinedLinkOptions) 96 | { 97 | if (builder == null) return null; 98 | if (propagatorBlock == null) throw new ArgumentNullException("propagatorBlock"); 99 | if (linkOptions == null) throw new ArgumentNullException("linkOptions"); 100 | if (predicate == null) throw new ArgumentNullException("predicate"); 101 | if (declinedTargetBlock == null) throw new ArgumentNullException("declinedTargetBlock"); 102 | if (declinedLinkOptions == null) throw new ArgumentNullException("declinedLinkOptions"); 103 | 104 | var nextBuilder = builder.LinkToPropagator(propagatorBlock, linkOptions, predicate); 105 | 106 | if (declinedTargetBlock != null) 107 | { 108 | // LinkTo declined target 109 | LinkHelper.Link((builder as PropagatorDataflowBuilder).FinalSourceBlock, declinedTargetBlock, declinedLinkOptions ?? DataflowDefaultOptions.DefaultLinkOptions); 110 | } 111 | 112 | return nextBuilder; 113 | } 114 | 115 | /// 116 | /// Links to a propagator block. 117 | /// 118 | /// 119 | /// 120 | /// 121 | /// 122 | /// 123 | /// 124 | /// 125 | /// 126 | public static IPropagatorDataflowBuilder LinkToPropagator( 127 | this IPropagatorDataflowBuilder builder 128 | , IPropagatorBlock propagatorBlock 129 | , Predicate predicate 130 | , ITargetBlock declinedTargetBlock = null) 131 | { 132 | return builder.LinkToPropagator(propagatorBlock, DataflowDefaultOptions.DefaultLinkOptions, predicate, declinedTargetBlock, DataflowDefaultOptions.DefaultLinkOptions); 133 | } 134 | 135 | #endregion 136 | 137 | #region ISourceDataflowBuilder link to ITargetBlock 138 | 139 | /// 140 | /// Links to a target block. 141 | /// 142 | /// 143 | /// 144 | /// 145 | /// 146 | /// 147 | /// 148 | /// 149 | /// 150 | public static IDataflowBuilder LinkToTarget( 151 | this ISourceDataflowBuilder builder 152 | , ITargetBlock targetBlock 153 | , DataflowLinkOptions linkOptions 154 | , Predicate predicate 155 | , ITargetBlock declinedTargetBlock 156 | , DataflowLinkOptions declinedLinkOptions) 157 | { 158 | if (builder == null) return null; 159 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 160 | if (linkOptions == null) throw new ArgumentNullException("linkOptions"); 161 | if (predicate == null) throw new ArgumentNullException("predicate"); 162 | if (declinedTargetBlock == null) throw new ArgumentNullException("declinedTargetBlock"); 163 | if (declinedLinkOptions == null) throw new ArgumentNullException("declinedLinkOptions"); 164 | 165 | var nextBuilder = builder.LinkToTarget(targetBlock, linkOptions, predicate); 166 | 167 | if (declinedTargetBlock != null) 168 | { 169 | // LinkTo declined target 170 | LinkHelper.Link((builder as SourceDataflowBuilder).FinalSourceBlock, declinedTargetBlock, declinedLinkOptions ?? DataflowDefaultOptions.DefaultLinkOptions); 171 | } 172 | 173 | return nextBuilder; 174 | } 175 | 176 | /// 177 | /// Links to a target block. 178 | /// 179 | /// 180 | /// 181 | /// 182 | /// 183 | /// 184 | /// 185 | public static IDataflowBuilder LinkToTarget( 186 | this ISourceDataflowBuilder builder 187 | , ITargetBlock targetBlock 188 | , Predicate predicate 189 | , ITargetBlock declinedTargetBlock = null) 190 | { 191 | return builder.LinkToTarget(targetBlock, DataflowDefaultOptions.DefaultLinkOptions, predicate, declinedTargetBlock, DataflowDefaultOptions.DefaultLinkOptions); 192 | } 193 | 194 | #endregion 195 | 196 | #region ISourceDataflowBuilder link to IPropagatorBlock 197 | 198 | /// 199 | /// Links to a propagator block. 200 | /// 201 | /// 202 | /// 203 | /// 204 | /// 205 | /// 206 | /// 207 | /// 208 | /// 209 | /// 210 | public static ISourceDataflowBuilder LinkToPropagator( 211 | this ISourceDataflowBuilder builder 212 | , IPropagatorBlock propagatorBlock 213 | , DataflowLinkOptions linkOptions 214 | , Predicate predicate 215 | , ITargetBlock declinedTargetBlock 216 | , DataflowLinkOptions declinedLinkOptions) 217 | { 218 | if (builder == null) return null; 219 | if (propagatorBlock == null) throw new ArgumentNullException("propagatorBlock"); 220 | if (linkOptions == null) throw new ArgumentNullException("linkOptions"); 221 | if (predicate == null) throw new ArgumentNullException("predicate"); 222 | if (declinedTargetBlock == null) throw new ArgumentNullException("declinedTargetBlock"); 223 | if (declinedLinkOptions == null) throw new ArgumentNullException("declinedLinkOptions"); 224 | 225 | var nextBuilder = builder.LinkToPropagator(propagatorBlock, linkOptions, predicate); 226 | 227 | if (declinedTargetBlock != null) 228 | { 229 | // LinkTo declined target 230 | LinkHelper.Link((builder as SourceDataflowBuilder).FinalSourceBlock, declinedTargetBlock, declinedLinkOptions ?? DataflowDefaultOptions.DefaultLinkOptions); 231 | } 232 | 233 | return nextBuilder; 234 | } 235 | 236 | /// 237 | /// Links to a propagator block. 238 | /// 239 | /// 240 | /// 241 | /// 242 | /// 243 | /// 244 | /// 245 | /// 246 | public static ISourceDataflowBuilder LinkToPropagator( 247 | this ISourceDataflowBuilder builder 248 | , IPropagatorBlock propagatorBlock 249 | , Predicate predicate 250 | , ITargetBlock declinedTargetBlock = null) 251 | { 252 | return builder.LinkToPropagator(propagatorBlock, DataflowDefaultOptions.DefaultLinkOptions, predicate, declinedTargetBlock, DataflowDefaultOptions.DefaultLinkOptions); 253 | } 254 | 255 | #endregion 256 | 257 | #region IMultipleSourceDataflowBuilder link to TargetBlock 258 | 259 | /// 260 | /// Links to a target block. 261 | /// 262 | /// 263 | /// 264 | /// 265 | /// 266 | /// 267 | /// 268 | /// 269 | /// 270 | public static IDataflowBuilder LinkToTarget( 271 | this IMultipleSourceDataflowBuilder builder 272 | , ITargetBlock targetBlock 273 | , DataflowLinkOptions linkOptions 274 | , Predicate predicate 275 | , ITargetBlock declinedTargetBlock 276 | , DataflowLinkOptions declinedLinkOptions) 277 | { 278 | if (builder == null) return null; 279 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 280 | if (linkOptions == null) throw new ArgumentNullException("linkOptions"); 281 | if (predicate == null) throw new ArgumentNullException("predicate"); 282 | if (declinedTargetBlock == null) throw new ArgumentNullException("declinedTargetBlock"); 283 | if (declinedLinkOptions == null) throw new ArgumentNullException("declinedLinkOptions"); 284 | 285 | var nextBuilder = builder.LinkToTarget(targetBlock, linkOptions, predicate); 286 | 287 | if (declinedTargetBlock != null) 288 | { 289 | // LinkTo declined target 290 | (builder as MultipleSourceDataflowBuilder).Link(declinedTargetBlock, declinedLinkOptions ?? DataflowDefaultOptions.DefaultLinkOptions, null); 291 | } 292 | 293 | return nextBuilder; 294 | } 295 | 296 | /// 297 | /// Links to a target block. 298 | /// 299 | /// 300 | /// 301 | /// 302 | /// 303 | /// 304 | /// 305 | public static IDataflowBuilder LinkToTarget( 306 | this IMultipleSourceDataflowBuilder builder 307 | , ITargetBlock targetBlock 308 | , Predicate predicate 309 | , ITargetBlock declinedTargetBlock = null) 310 | { 311 | return builder.LinkToTarget(targetBlock, DataflowDefaultOptions.DefaultLinkOptions, predicate, declinedTargetBlock, DataflowDefaultOptions.DefaultLinkOptions); 312 | } 313 | 314 | #endregion 315 | 316 | #region IBroadcastDataflowBuilder link to ITargetBlock 317 | 318 | /// 319 | /// Links to a target block. 320 | /// 321 | /// 322 | /// 323 | /// 324 | /// 325 | /// 326 | /// 327 | /// 328 | /// 329 | public static IBroadcastDataflowBuilder LinkTo( 330 | this IBroadcastDataflowBuilder builder 331 | , ITargetBlock targetBlock 332 | , DataflowLinkOptions linkOptions 333 | , Predicate predicate 334 | , ITargetBlock declinedTargetBlock 335 | , DataflowLinkOptions declinedLinkOptions) 336 | { 337 | if (builder == null) return null; 338 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 339 | if (linkOptions == null) throw new ArgumentNullException("linkOptions"); 340 | if (predicate == null) throw new ArgumentNullException("predicate"); 341 | if (declinedTargetBlock == null) throw new ArgumentNullException("declinedTargetBlock"); 342 | if (declinedLinkOptions == null) throw new ArgumentNullException("declinedLinkOptions"); 343 | 344 | var nextBuilder = builder.LinkTo(targetBlock, linkOptions, predicate); 345 | 346 | if (declinedTargetBlock != null) 347 | { 348 | // LinkTo declined target 349 | LinkHelper.Link((builder as BroadcastDataflowBuilder).BroadcastBock, declinedTargetBlock, declinedLinkOptions ?? DataflowDefaultOptions.DefaultLinkOptions); 350 | } 351 | 352 | return nextBuilder; 353 | } 354 | 355 | /// 356 | /// Links to a target block. 357 | /// 358 | /// 359 | /// 360 | /// 361 | /// 362 | /// 363 | /// 364 | public static IBroadcastDataflowBuilder LinkTo( 365 | this IBroadcastDataflowBuilder builder 366 | , ITargetBlock targetBlock 367 | , Predicate predicate 368 | , ITargetBlock declinedTargetBlock = null) 369 | { 370 | return builder.LinkTo(targetBlock, DataflowDefaultOptions.DefaultLinkOptions, predicate, declinedTargetBlock, DataflowDefaultOptions.DefaultLinkOptions); 371 | } 372 | 373 | #endregion 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowDefaultOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks.Dataflow; 2 | 3 | namespace FluentDataflow 4 | { 5 | /// 6 | /// Default dataflow options. 7 | /// 8 | public static class DataflowDefaultOptions 9 | { 10 | /// 11 | /// Default block options. 12 | /// 13 | public static ExecutionDataflowBlockOptions DefaultBlockOptions => new ExecutionDataflowBlockOptions(); 14 | 15 | /// 16 | /// Default link options. 17 | /// 18 | public static DataflowLinkOptions DefaultLinkOptions => new DataflowLinkOptions { PropagateCompletion = true }; 19 | 20 | /// 21 | /// Default grouping block options. 22 | /// 23 | public static GroupingDataflowBlockOptions DefaultGroupingBlockOptions => new GroupingDataflowBlockOptions(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks.Dataflow; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace FluentDataflow 7 | { 8 | /// 9 | /// The implementation of . 10 | /// 11 | public class DataflowFactory : IDataflowFactory 12 | { 13 | /// 14 | /// Creates a dataflow builder from multiple source blocks, the output of all the sources are merged into the pipeline. 15 | /// 16 | /// 17 | /// 18 | /// 19 | public IMultipleSourceDataflowBuilder FromMultipleSources(params ISourceBlock[] sourceBlocks) 20 | { 21 | if (sourceBlocks == null || sourceBlocks.Length == 0) throw new ArgumentNullException("sourceBlocks"); 22 | 23 | return new MultipleSourceDataflowBuilder(sourceBlocks); 24 | } 25 | 26 | /// 27 | /// Creates a dataflow block from a propagator block. 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | public IPropagatorDataflowBuilder FromPropagator(IPropagatorBlock propagatorBlock) 34 | { 35 | if (propagatorBlock == null) throw new ArgumentNullException("propagatorBlock"); 36 | 37 | return new PropagatorDataflowBuilder(propagatorBlock); 38 | } 39 | 40 | /// 41 | /// Creates a dataflow builder from a source block. 42 | /// 43 | /// 44 | /// 45 | /// 46 | public ISourceDataflowBuilder FromSource(ISourceBlock sourceBlock) 47 | { 48 | if (sourceBlock == null) throw new ArgumentNullException("sourceBlock"); 49 | 50 | return new SourceDataflowBuilder(sourceBlock); 51 | } 52 | 53 | /// 54 | /// Creates a dataflow builder from a target block. 55 | /// 56 | /// 57 | /// 58 | /// 59 | public ITargetDataflowBuilder FromTarget(ITargetBlock targetBlock) 60 | { 61 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 62 | 63 | return new TargetDataflowBuilder(targetBlock); 64 | } 65 | 66 | /// 67 | /// Creates a dataflow builder from a join of multiple source blocks. 68 | /// 69 | /// 70 | /// 71 | /// 72 | /// 73 | /// 74 | /// 75 | public ISourceDataflowBuilder> Join(ISourceBlock sourceBlock1, ISourceBlock sourceBlock2, DataflowJoinOptions joinOptions = default(DataflowJoinOptions)) 76 | { 77 | if (sourceBlock1 == null) throw new ArgumentNullException("sourceBlock1"); 78 | if (sourceBlock2 == null) throw new ArgumentNullException("sourceBlock2"); 79 | 80 | var joinBlock = new JoinBlock(joinOptions.JoinBlockOptions); 81 | LinkHelper.Link(sourceBlock1, joinBlock.Target1, joinOptions.Target1LinkOptions); 82 | LinkHelper.Link(sourceBlock2, joinBlock.Target2, joinOptions.Target2LinkOptions); 83 | 84 | var multipleSourcesWrapper = new MultipleSourceDataflowWrapper(new IDataflowBlock[] { sourceBlock1, sourceBlock2 }); 85 | var sourceWrapper = new ReceivableSourceDataflowWrapper>(multipleSourcesWrapper, joinBlock, joinBlock); 86 | 87 | return FromSource(sourceWrapper); 88 | } 89 | 90 | /// 91 | /// Creates a dataflow builder from a join of multiple source blocks. 92 | /// 93 | /// 94 | /// 95 | /// 96 | /// 97 | /// 98 | /// 99 | /// 100 | /// 101 | public ISourceDataflowBuilder> Join(ISourceBlock sourceBlock1, ISourceBlock sourceBlock2, ISourceBlock sourceBlock3, DataflowJoinOptions joinOptions = default(DataflowJoinOptions)) 102 | { 103 | if (sourceBlock1 == null) throw new ArgumentNullException("sourceBlock1"); 104 | if (sourceBlock2 == null) throw new ArgumentNullException("sourceBlock2"); 105 | if (sourceBlock3 == null) throw new ArgumentNullException("sourceBlock3"); 106 | 107 | var joinBlock = new JoinBlock(joinOptions.JoinBlockOptions); 108 | LinkHelper.Link(sourceBlock1, joinBlock.Target1, joinOptions.Target1LinkOptions); 109 | LinkHelper.Link(sourceBlock2, joinBlock.Target2, joinOptions.Target2LinkOptions); 110 | LinkHelper.Link(sourceBlock3, joinBlock.Target3, joinOptions.Target3LinkOptions); 111 | 112 | var multipleSourcesWrapper = new MultipleSourceDataflowWrapper(new IDataflowBlock[] { sourceBlock1, sourceBlock2, sourceBlock3 }); 113 | var sourceWrapper = new ReceivableSourceDataflowWrapper>(multipleSourcesWrapper, joinBlock, joinBlock); 114 | 115 | return FromSource(sourceWrapper); 116 | } 117 | 118 | /// 119 | /// Creates a dataflow builder from a batched join of multiple source blocks. 120 | /// 121 | /// 122 | /// 123 | /// 124 | /// 125 | /// 126 | /// 127 | /// 128 | public ISourceDataflowBuilder, IList>> BatchedJoin(ISourceBlock sourceBlock1, ISourceBlock sourceBlock2, int batchSize, DataflowJoinOptions joinOptions = default(DataflowJoinOptions)) 129 | { 130 | if (sourceBlock1 == null) throw new ArgumentNullException("sourceBlock1"); 131 | if (sourceBlock2 == null) throw new ArgumentNullException("sourceBlock2"); 132 | 133 | var batchedJoinBlock = new BatchedJoinBlock(batchSize, joinOptions.JoinBlockOptions); 134 | LinkHelper.Link(sourceBlock1, batchedJoinBlock.Target1, joinOptions.Target1LinkOptions); 135 | LinkHelper.Link(sourceBlock2, batchedJoinBlock.Target2, joinOptions.Target2LinkOptions); 136 | 137 | var multipleSourcesWrapper = new MultipleSourceDataflowWrapper(new IDataflowBlock[] { sourceBlock1, sourceBlock2 }); 138 | var sourceWrapper = new ReceivableSourceDataflowWrapper, IList>>(multipleSourcesWrapper, batchedJoinBlock, batchedJoinBlock); 139 | 140 | return FromSource(sourceWrapper); 141 | } 142 | 143 | /// 144 | /// Creates a dataflow builder from a batched join of multiple source blocks. 145 | /// 146 | /// 147 | /// 148 | /// 149 | /// 150 | /// 151 | /// 152 | /// 153 | /// 154 | /// 155 | public ISourceDataflowBuilder, IList, IList>> BatchedJoin(ISourceBlock sourceBlock1, ISourceBlock sourceBlock2, ISourceBlock sourceBlock3, int batchSize, DataflowJoinOptions joinOptions = default(DataflowJoinOptions)) 156 | { 157 | if (sourceBlock1 == null) throw new ArgumentNullException("sourceBlock1"); 158 | if (sourceBlock2 == null) throw new ArgumentNullException("sourceBlock2"); 159 | if (sourceBlock3 == null) throw new ArgumentNullException("sourceBlock3"); 160 | 161 | var batchedJoinBlock = new BatchedJoinBlock(batchSize, joinOptions.JoinBlockOptions); 162 | LinkHelper.Link(sourceBlock1, batchedJoinBlock.Target1, joinOptions.Target1LinkOptions); 163 | LinkHelper.Link(sourceBlock2, batchedJoinBlock.Target2, joinOptions.Target2LinkOptions); 164 | LinkHelper.Link(sourceBlock3, batchedJoinBlock.Target3, joinOptions.Target3LinkOptions); 165 | 166 | var multipleSourcesWrapper = new MultipleSourceDataflowWrapper(new IDataflowBlock[] { sourceBlock1, sourceBlock2, sourceBlock3 }); 167 | var sourceWrapper = new ReceivableSourceDataflowWrapper, IList, IList>>(multipleSourcesWrapper, batchedJoinBlock, batchedJoinBlock); 168 | 169 | return FromSource(sourceWrapper); 170 | } 171 | 172 | /// 173 | /// Createsa dataflow builder from a broadcast block. 174 | /// 175 | /// 176 | /// 177 | /// 178 | public IBroadcastDataflowBuilder FromBroadcast(BroadcastBlock broadcastBlock) 179 | { 180 | if (broadcastBlock == null) throw new ArgumentNullException("broadcastBlock"); 181 | 182 | return new BroadcastDataflowBuilder(broadcastBlock); 183 | } 184 | 185 | /// 186 | /// Encapsulates custom dataflow block head and tail as one dataflow block. 187 | /// 188 | /// 189 | /// 190 | /// 191 | public IDataflowBlock EncapsulateDataflow(IDataflowBlock head, IDataflowBlock tail) 192 | { 193 | if (head == null) throw new ArgumentNullException("head"); 194 | if (tail == null) throw new ArgumentNullException("tail"); 195 | 196 | return new DataflowWrapper(head, head, tail); 197 | } 198 | 199 | /// 200 | /// Encapsulates custom dataflow block multiple heads and a tail as one dataflow block. 201 | /// 202 | /// 203 | /// 204 | /// 205 | public IDataflowBlock EncapsulateDataflow(IEnumerable multipleHeads, IDataflowBlock tail) 206 | { 207 | if (multipleHeads == null || !multipleHeads.Any()) throw new ArgumentNullException("multipleHeads"); 208 | if (tail == null) throw new ArgumentNullException("tail"); 209 | 210 | return EncapsulateDataflow(new MultipleSourceDataflowWrapper(multipleHeads.ToArray()), tail); 211 | } 212 | 213 | /// 214 | /// Encapsulates custom dataflow block head and tail as one dataflow block. 215 | /// 216 | /// 217 | /// 218 | /// 219 | /// 220 | public ISourceBlock EncapsulateSourceDataflow(IDataflowBlock head, ISourceBlock tail) 221 | { 222 | if (head == null) throw new ArgumentNullException("head"); 223 | if (tail == null) throw new ArgumentNullException("tail"); 224 | 225 | if (tail is IReceivableSourceBlock) 226 | return new ReceivableSourceDataflowWrapper(head, head, tail); 227 | else 228 | return new SourceDataflowWrapper(head, head, tail); 229 | } 230 | 231 | /// 232 | /// Encapsulates custom dataflow block multiple heads and a tail as one dataflow block. 233 | /// 234 | /// 235 | /// 236 | /// 237 | /// 238 | public ISourceBlock EncapsulateSourceDataflow(IEnumerable multipleHeads, ISourceBlock tail) 239 | { 240 | if (multipleHeads == null || !multipleHeads.Any()) throw new ArgumentNullException("multipleHeads"); 241 | if (tail == null) throw new ArgumentNullException("tail"); 242 | 243 | return EncapsulateSourceDataflow(new MultipleSourceDataflowWrapper(multipleHeads.ToArray()), tail); 244 | } 245 | 246 | /// 247 | /// Encapsulates custom dataflow block head and tail as one dataflow block. 248 | /// 249 | /// 250 | /// 251 | /// 252 | /// 253 | public ITargetBlock EncapsulateTargetDataflow(ITargetBlock head, IDataflowBlock tail) 254 | { 255 | if (head == null) throw new ArgumentNullException("head"); 256 | if (tail == null) throw new ArgumentNullException("tail"); 257 | 258 | return new TargetDataflowWrapper(head, tail); 259 | } 260 | 261 | /// 262 | /// Encapsulates custom dataflow block head and tail as one dataflow block. 263 | /// 264 | /// 265 | /// 266 | /// 267 | /// 268 | /// 269 | public IPropagatorBlock EncapsulatePropagatorDataflow(ITargetBlock head, ISourceBlock tail) 270 | { 271 | if (head == null) throw new ArgumentNullException("head"); 272 | if (tail == null) throw new ArgumentNullException("tail"); 273 | 274 | return DataflowBlock.Encapsulate(head, tail); 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace FluentDataflow 7 | { 8 | /// 9 | /// Extension methods for . 10 | /// 11 | public static class DataflowFactoryExtensions 12 | { 13 | /// 14 | /// Creates a dataflow builder from a cloning function. 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | public static IBroadcastDataflowBuilder FromBroadcast( 22 | this IDataflowFactory factory 23 | , Func cloningFunction = null 24 | , DataflowBlockOptions blockOptions = null) 25 | { 26 | var broadcastBlock = new BroadcastBlock(cloningFunction ?? new Func(t => t), blockOptions ?? DataflowDefaultOptions.DefaultBlockOptions); 27 | return factory.FromBroadcast(broadcastBlock); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowJoinOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks.Dataflow; 3 | 4 | namespace FluentDataflow 5 | { 6 | /// 7 | /// Options for applying join. 8 | /// 9 | public struct DataflowJoinOptions 10 | { 11 | private GroupingDataflowBlockOptions _joinOptions; 12 | private DataflowLinkOptions _target1LinkOptions; 13 | private DataflowLinkOptions _target2LinkOptions; 14 | private DataflowLinkOptions _target3LinkOptions; 15 | 16 | /// 17 | /// Initializes a DataflowJoinOptions. 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | public DataflowJoinOptions( 24 | Action join = null 25 | , Action target1 = null 26 | , Action target2 = null 27 | , Action target3 = null) 28 | { 29 | _joinOptions = DataflowDefaultOptions.DefaultGroupingBlockOptions; 30 | _target1LinkOptions = DataflowDefaultOptions.DefaultLinkOptions; 31 | _target2LinkOptions = DataflowDefaultOptions.DefaultLinkOptions; 32 | _target3LinkOptions = DataflowDefaultOptions.DefaultLinkOptions; 33 | 34 | if (join != null) join(_joinOptions); 35 | if (target1 != null) target1(_target1LinkOptions); 36 | if (target2 != null) target2(_target2LinkOptions); 37 | if (target3 != null) target3(_target3LinkOptions); 38 | } 39 | 40 | /// 41 | /// Block options for creating join block. 42 | /// 43 | public GroupingDataflowBlockOptions JoinBlockOptions 44 | { 45 | get 46 | { 47 | return _joinOptions ?? (_joinOptions = DataflowDefaultOptions.DefaultGroupingBlockOptions); 48 | } 49 | set 50 | { 51 | _joinOptions = value; 52 | } 53 | } 54 | 55 | /// 56 | /// Link options for linking to the join block. 57 | /// 58 | public DataflowLinkOptions Target1LinkOptions 59 | { 60 | get 61 | { 62 | return _target1LinkOptions ?? (_target1LinkOptions = DataflowDefaultOptions.DefaultLinkOptions); 63 | } 64 | set 65 | { 66 | _target1LinkOptions = value; 67 | } 68 | } 69 | 70 | /// 71 | /// Link options for linking to the join block. 72 | /// 73 | public DataflowLinkOptions Target2LinkOptions 74 | { 75 | get 76 | { 77 | return _target2LinkOptions ?? (_target2LinkOptions = DataflowDefaultOptions.DefaultLinkOptions); 78 | } 79 | set 80 | { 81 | _target2LinkOptions = value; 82 | } 83 | } 84 | 85 | /// 86 | /// Link options for linking to the join block. 87 | /// 88 | public DataflowLinkOptions Target3LinkOptions 89 | { 90 | get 91 | { 92 | return _target3LinkOptions ?? (_target3LinkOptions = DataflowDefaultOptions.DefaultLinkOptions); 93 | } 94 | set 95 | { 96 | _target3LinkOptions = value; 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace FluentDataflow 9 | { 10 | /// 11 | /// Extensions methods for fluently create data flow options 12 | /// 13 | public static class DataflowOptionsExtensions 14 | { 15 | #region ExecutionDataflowBlockOptions 16 | 17 | /// 18 | /// Sets MaxDegreeOfParallelism 19 | /// 20 | /// 21 | /// 22 | /// 23 | public static ExecutionDataflowBlockOptions MaxDegreeOfParallelism(this ExecutionDataflowBlockOptions options, int val) 24 | { 25 | if (options == null) return null; 26 | 27 | options.MaxDegreeOfParallelism = val; 28 | 29 | return options; 30 | } 31 | 32 | /// 33 | /// Sets SingleProducerConstrained 34 | /// 35 | /// 36 | /// 37 | /// 38 | public static ExecutionDataflowBlockOptions SingleProducerConstrained(this ExecutionDataflowBlockOptions options, bool val = true) 39 | { 40 | if (options == null) return null; 41 | 42 | options.SingleProducerConstrained = val; 43 | 44 | return options; 45 | } 46 | 47 | /// 48 | /// Sets TaskScheduler 49 | /// 50 | /// 51 | /// 52 | /// 53 | public static ExecutionDataflowBlockOptions TaskScheduler(this ExecutionDataflowBlockOptions options, TaskScheduler val) 54 | { 55 | if (options == null) return null; 56 | 57 | options.TaskScheduler = val; 58 | 59 | return options; 60 | } 61 | 62 | /// 63 | /// Sets CancellationToken 64 | /// 65 | /// 66 | /// 67 | /// 68 | public static ExecutionDataflowBlockOptions CancellationToken(this ExecutionDataflowBlockOptions options, CancellationToken val) 69 | { 70 | if (options == null) return null; 71 | 72 | options.CancellationToken = val; 73 | 74 | return options; 75 | } 76 | 77 | /// 78 | /// Sets MaxMessagesPerTask 79 | /// 80 | /// 81 | /// 82 | /// 83 | public static ExecutionDataflowBlockOptions MaxMessagesPerTask(this ExecutionDataflowBlockOptions options, int val) 84 | { 85 | if (options == null) return null; 86 | 87 | options.MaxMessagesPerTask = val; 88 | 89 | return options; 90 | } 91 | 92 | /// 93 | /// Sets BoundedCapacity 94 | /// 95 | /// 96 | /// 97 | /// 98 | public static ExecutionDataflowBlockOptions BoundedCapacity(this ExecutionDataflowBlockOptions options, int val) 99 | { 100 | if (options == null) return null; 101 | 102 | options.BoundedCapacity = val; 103 | 104 | return options; 105 | } 106 | 107 | /// 108 | /// Sets NameFormat 109 | /// 110 | /// 111 | /// 112 | /// 113 | public static ExecutionDataflowBlockOptions NameFormat(this ExecutionDataflowBlockOptions options, string val) 114 | { 115 | if (options == null) return null; 116 | 117 | options.NameFormat = val; 118 | 119 | return options; 120 | } 121 | 122 | /// 123 | /// Sets EnsureOrdered 124 | /// 125 | /// 126 | /// 127 | /// 128 | public static ExecutionDataflowBlockOptions EnsureOrdered(this ExecutionDataflowBlockOptions options, bool val = true) 129 | { 130 | if (options == null) return null; 131 | 132 | options.EnsureOrdered = val; 133 | 134 | return options; 135 | } 136 | 137 | #endregion 138 | 139 | #region GroupingDataflowBlockOptions 140 | 141 | /// 142 | /// Sets MaxNumberOfGroups 143 | /// 144 | /// 145 | /// 146 | /// 147 | public static GroupingDataflowBlockOptions MaxNumberOfGroups(this GroupingDataflowBlockOptions options, int val) 148 | { 149 | if (options == null) return null; 150 | 151 | options.MaxNumberOfGroups = val; 152 | 153 | return options; 154 | } 155 | 156 | /// 157 | /// Sets Greedy 158 | /// 159 | /// 160 | /// 161 | /// 162 | public static GroupingDataflowBlockOptions Greedy(this GroupingDataflowBlockOptions options, bool val = true) 163 | { 164 | if (options == null) return null; 165 | 166 | options.Greedy = val; 167 | 168 | return options; 169 | } 170 | 171 | /// 172 | /// Sets TaskScheduler 173 | /// 174 | /// 175 | /// 176 | /// 177 | public static GroupingDataflowBlockOptions TaskScheduler(this GroupingDataflowBlockOptions options, TaskScheduler val) 178 | { 179 | if (options == null) return null; 180 | 181 | options.TaskScheduler = val; 182 | 183 | return options; 184 | } 185 | 186 | /// 187 | /// Sets CancellationToken 188 | /// 189 | /// 190 | /// 191 | /// 192 | public static GroupingDataflowBlockOptions CancellationToken(this GroupingDataflowBlockOptions options, CancellationToken val) 193 | { 194 | if (options == null) return null; 195 | 196 | options.CancellationToken = val; 197 | 198 | return options; 199 | } 200 | 201 | /// 202 | /// Sets MaxMessagesPerTask 203 | /// 204 | /// 205 | /// 206 | /// 207 | public static GroupingDataflowBlockOptions MaxMessagesPerTask(this GroupingDataflowBlockOptions options, int val) 208 | { 209 | if (options == null) return null; 210 | 211 | options.MaxMessagesPerTask = val; 212 | 213 | return options; 214 | } 215 | 216 | /// 217 | /// Sets BoundedCapacity 218 | /// 219 | /// 220 | /// 221 | /// 222 | public static GroupingDataflowBlockOptions BoundedCapacity(this GroupingDataflowBlockOptions options, int val) 223 | { 224 | if (options == null) return null; 225 | 226 | options.BoundedCapacity = val; 227 | 228 | return options; 229 | } 230 | 231 | /// 232 | /// Sets NameFormat 233 | /// 234 | /// 235 | /// 236 | /// 237 | public static GroupingDataflowBlockOptions NameFormat(this GroupingDataflowBlockOptions options, string val) 238 | { 239 | if (options == null) return null; 240 | 241 | options.NameFormat = val; 242 | 243 | return options; 244 | } 245 | 246 | /// 247 | /// Sets EnsureOrdered 248 | /// 249 | /// 250 | /// 251 | /// 252 | public static GroupingDataflowBlockOptions EnsureOrdered(this GroupingDataflowBlockOptions options, bool val = true) 253 | { 254 | if (options == null) return null; 255 | 256 | options.EnsureOrdered = val; 257 | 258 | return options; 259 | } 260 | 261 | #endregion 262 | 263 | #region DataflowLinkOptions 264 | 265 | /// 266 | /// Sets PropagateCompletion 267 | /// 268 | /// 269 | /// 270 | /// 271 | public static DataflowLinkOptions PropagateCompletion(this DataflowLinkOptions options, bool val = true) 272 | { 273 | if (options == null) return null; 274 | 275 | options.PropagateCompletion = val; 276 | 277 | return options; 278 | } 279 | 280 | /// 281 | /// Sets Append 282 | /// 283 | /// 284 | /// 285 | /// 286 | public static DataflowLinkOptions Append(this DataflowLinkOptions options, bool val = true) 287 | { 288 | if (options == null) return null; 289 | 290 | options.Append = val; 291 | 292 | return options; 293 | } 294 | 295 | /// 296 | /// Sets MaxMessages 297 | /// 298 | /// 299 | /// 300 | /// 301 | public static DataflowLinkOptions MaxMessages(this DataflowLinkOptions options, int val) 302 | { 303 | if (options == null) return null; 304 | 305 | options.MaxMessages = val; 306 | 307 | return options; 308 | } 309 | 310 | #endregion 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow 6 | { 7 | internal class DataflowWrapper : IDataflowBlock 8 | { 9 | private readonly IDataflowBlock _originalSourceBlock; 10 | private readonly IDataflowBlock _currentSourceBlock; 11 | private readonly IDataflowBlock _targetBlock; 12 | private readonly bool? _propagateCompletion; 13 | 14 | public DataflowWrapper(IDataflowBlock originalSourceBlock, IDataflowBlock currentSourceBlock, IDataflowBlock targetBlock, bool? propagateCompletion = null) 15 | { 16 | _originalSourceBlock = originalSourceBlock; 17 | _currentSourceBlock = currentSourceBlock; 18 | _targetBlock = targetBlock; 19 | _propagateCompletion = propagateCompletion; 20 | } 21 | 22 | public Task Completion 23 | { 24 | get 25 | { 26 | if (_propagateCompletion.GetValueOrDefault()) 27 | { 28 | return _currentSourceBlock.Completion.ContinueWith(task => 29 | { 30 | if (task.IsFaulted) 31 | _targetBlock.Fault(task.Exception); 32 | else 33 | _targetBlock.Complete(); 34 | 35 | return _targetBlock.Completion; 36 | }); 37 | } 38 | 39 | return _targetBlock.Completion; 40 | } 41 | } 42 | 43 | public void Complete() 44 | { 45 | _originalSourceBlock.Complete(); 46 | } 47 | 48 | public void Fault(Exception exception) 49 | { 50 | _originalSourceBlock.Fault(exception); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /FluentDataflow/DataflowWriteOnceOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks.Dataflow; 3 | 4 | namespace FluentDataflow 5 | { 6 | /// 7 | /// Options for applying write once. 8 | /// 9 | public struct DataflowWriteOnceOptions 10 | { 11 | private GroupingDataflowBlockOptions _writeOnceOptions; 12 | private DataflowLinkOptions _linkOptions; 13 | 14 | /// 15 | /// Initializes a DataflowWriteOnceOptions. 16 | /// 17 | /// 18 | /// 19 | public DataflowWriteOnceOptions(Action writeOnce = null 20 | , Action link = null) 21 | { 22 | _writeOnceOptions = DataflowDefaultOptions.DefaultGroupingBlockOptions; 23 | _linkOptions = DataflowDefaultOptions.DefaultLinkOptions; 24 | 25 | if (writeOnce != null) writeOnce(_writeOnceOptions); 26 | if (link != null) link(_linkOptions); 27 | } 28 | 29 | /// 30 | /// Block options for creating write once block. 31 | /// 32 | public GroupingDataflowBlockOptions WriteOnceBlockOptions 33 | { 34 | get 35 | { 36 | return _writeOnceOptions ?? (_writeOnceOptions = DataflowDefaultOptions.DefaultGroupingBlockOptions); 37 | } 38 | set 39 | { 40 | _writeOnceOptions = value; 41 | } 42 | } 43 | 44 | /// 45 | /// Link options for linking to the write once block. 46 | /// 47 | public DataflowLinkOptions LinkOptions 48 | { 49 | get 50 | { 51 | return _linkOptions ?? (_linkOptions = DataflowDefaultOptions.DefaultLinkOptions); 52 | } 53 | set 54 | { 55 | _linkOptions = value; 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /FluentDataflow/FluentDataflow.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 1.0.7.0 6 | A fluent style wrapper and extension of TPL Dataflow. 7 | https://github.com/teddymacn/FluentDataflow 8 | https://mit-license.org/ 9 | Teddy Ma 10 | https://github.com/teddymacn/FluentDataflow 11 | 1.0.7 12 | 13 | 14 | 15 | bin\Release\netstandard2.0\FluentDataflow.xml 16 | 17 | 18 | 19 | bin\Debug\netstandard2.0\FluentDataflow.xml 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /FluentDataflow/FluentDataflow.nuspec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teddymacn/FluentDataflow/e281e66fb756a1607323d7cd64e5fde2274cd0b3/FluentDataflow/FluentDataflow.nuspec -------------------------------------------------------------------------------- /FluentDataflow/IDataflowBuilder.cs: -------------------------------------------------------------------------------- 1 | using FluentDataflow; 2 | using System; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow 6 | { 7 | /// 8 | /// Represents a dataflow builder to create a dataflow with no input and no output. 9 | /// 10 | public interface IDataflowBuilder 11 | { 12 | /// 13 | /// Creates a dataflow from builder. 14 | /// 15 | /// 16 | IDataflowBlock Create(); 17 | } 18 | 19 | /// 20 | /// Represents a dataflow builder to create a dataflow with no input but with output. 21 | /// 22 | /// 23 | public interface ISourceDataflowBuilder 24 | { 25 | /// 26 | /// Links to a target block. 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | IDataflowBuilder LinkToTarget(ITargetBlock targetBlock, DataflowLinkOptions linkOptions = null, Predicate predicate = null); 33 | 34 | /// 35 | /// Links to a propagator block. 36 | /// 37 | /// 38 | /// 39 | /// 40 | /// 41 | /// 42 | ISourceDataflowBuilder LinkToPropagator(IPropagatorBlock propagatorBlock, DataflowLinkOptions linkOptions = null, Predicate predicate = null); 43 | 44 | /// 45 | /// Creates a dataflow from builder. 46 | /// 47 | /// 48 | ISourceBlock Create(); 49 | 50 | /// 51 | /// Batches the current pipeline. 52 | /// 53 | /// 54 | /// 55 | /// 56 | ISourceDataflowBuilder Batch(int batchSize, DataflowBatchOptions batchOptions = default(DataflowBatchOptions)); 57 | 58 | /// 59 | /// Ensures write once on the current pipeline. 60 | /// 61 | /// 62 | /// 63 | /// 64 | ISourceDataflowBuilder WriteOnce(Func cloningFunction, DataflowWriteOnceOptions writeOnceOptions = default(DataflowWriteOnceOptions)); 65 | } 66 | 67 | /// 68 | /// Represents a dataflow builder to create a dataflow with input but with no output. 69 | /// 70 | /// 71 | public interface ITargetDataflowBuilder 72 | { 73 | /// 74 | /// Creates a dataflow from builder. 75 | /// 76 | /// 77 | ITargetBlock Create(); 78 | } 79 | 80 | /// 81 | /// Represents a dataflow builder to create a dataflow with both input and output. 82 | /// 83 | /// 84 | /// 85 | public interface IPropagatorDataflowBuilder 86 | { 87 | /// 88 | /// Links to a target block. 89 | /// 90 | /// 91 | /// 92 | /// 93 | /// 94 | ITargetDataflowBuilder LinkToTarget(ITargetBlock targetBlock, DataflowLinkOptions linkOptions = null, Predicate predicate = null); 95 | 96 | /// 97 | /// Links to a propagator block. 98 | /// 99 | /// 100 | /// 101 | /// 102 | /// 103 | /// 104 | IPropagatorDataflowBuilder LinkToPropagator(IPropagatorBlock propagatorBlock, DataflowLinkOptions linkOptions = null, Predicate predicate = null); 105 | 106 | /// 107 | /// Creates a data flow from current builder. 108 | /// 109 | /// 110 | IPropagatorBlock Create(); 111 | 112 | /// 113 | /// Batches the current pipeline. 114 | /// 115 | /// 116 | /// 117 | /// 118 | IPropagatorDataflowBuilder Batch(int batchSize, DataflowBatchOptions batchOptions = default(DataflowBatchOptions)); 119 | 120 | /// 121 | /// Ensures write once on the current pipeline. 122 | /// 123 | /// 124 | /// 125 | /// 126 | IPropagatorDataflowBuilder WriteOnce(Func cloningFunction, DataflowWriteOnceOptions writeOnceOptions = default(DataflowWriteOnceOptions)); 127 | } 128 | 129 | /// 130 | /// Represents a dataflow builder to create a dataflow with multiple outputs and no input. 131 | /// 132 | /// 133 | public interface IMultipleSourceDataflowBuilder 134 | { 135 | /// 136 | /// Links to a target block. 137 | /// 138 | /// 139 | /// 140 | /// 141 | /// 142 | IDataflowBuilder LinkToTarget(ITargetBlock targetBlock, DataflowLinkOptions linkOptions = null, Predicate predicate = null); 143 | 144 | /// 145 | /// Links to a propagator block. 146 | /// 147 | /// 148 | /// 149 | /// 150 | /// 151 | /// 152 | ISourceDataflowBuilder LinkToPropagator(IPropagatorBlock propagatorBlock, DataflowLinkOptions linkOptions = null, Predicate predicate = null); 153 | 154 | /// 155 | /// Batches the current pipeline. 156 | /// 157 | /// 158 | /// 159 | /// 160 | ISourceDataflowBuilder Batch(int batchSize, DataflowBatchOptions batchOptions = default(DataflowBatchOptions)); 161 | 162 | /// 163 | /// Ensures write once on the current pipeline. 164 | /// 165 | /// 166 | /// 167 | /// 168 | ISourceDataflowBuilder WriteOnce(Func cloningFunction, DataflowWriteOnceOptions writeOnceOptions = default(DataflowWriteOnceOptions)); 169 | } 170 | 171 | /// 172 | /// Represents a dataflow builder to create a dataflow with a broadcast input and multiple outputs. 173 | /// 174 | /// 175 | public interface IBroadcastDataflowBuilder 176 | { 177 | /// 178 | /// Links to a target block. 179 | /// 180 | /// 181 | /// 182 | /// 183 | /// 184 | IBroadcastDataflowBuilder LinkTo(ITargetBlock targetBlock, DataflowLinkOptions linkOptions = null, Predicate predicate = null); 185 | 186 | /// 187 | /// Creates a dataflow from builder. 188 | /// 189 | /// 190 | ITargetBlock Create(); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /FluentDataflow/IDataflowFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow 6 | { 7 | /// 8 | /// Represents a dataflow factory. 9 | /// 10 | public interface IDataflowFactory 11 | { 12 | /// 13 | /// Creates a dataflow builder from a source block. 14 | /// 15 | /// 16 | /// 17 | /// 18 | ISourceDataflowBuilder FromSource(ISourceBlock sourceBlock); 19 | 20 | /// 21 | /// Creates a dataflow builder from a target block. 22 | /// 23 | /// 24 | /// 25 | /// 26 | ITargetDataflowBuilder FromTarget(ITargetBlock targetBlock); 27 | 28 | /// 29 | /// Creates a dataflow block from a propagator block. 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | IPropagatorDataflowBuilder FromPropagator(IPropagatorBlock propagatorBlock); 36 | 37 | /// 38 | /// Creates a dataflow builder from multiple source blocks, the output of all the sources are merged into the pipeline. 39 | /// 40 | /// 41 | /// 42 | /// 43 | IMultipleSourceDataflowBuilder FromMultipleSources(params ISourceBlock[] sourceBlocks); 44 | 45 | /// 46 | /// Creates a dataflow builder from a join of multiple source blocks. 47 | /// 48 | /// 49 | /// 50 | /// 51 | /// 52 | /// 53 | /// 54 | ISourceDataflowBuilder> Join(ISourceBlock sourceBlock1, ISourceBlock sourceBlock2, DataflowJoinOptions joinOptions = default(DataflowJoinOptions)); 55 | 56 | /// 57 | /// Creates a dataflow builder from a join of multiple source blocks. 58 | /// 59 | /// 60 | /// 61 | /// 62 | /// 63 | /// 64 | /// 65 | /// 66 | /// 67 | ISourceDataflowBuilder> Join(ISourceBlock sourceBlock1, ISourceBlock sourceBlock2, ISourceBlock sourceBlock3, DataflowJoinOptions joinOptions = default(DataflowJoinOptions)); 68 | 69 | /// 70 | /// Creates a dataflow builder from a batched join of multiple source blocks. 71 | /// 72 | /// 73 | /// 74 | /// 75 | /// 76 | /// 77 | /// 78 | /// 79 | ISourceDataflowBuilder, IList>> BatchedJoin(ISourceBlock sourceBlock1, ISourceBlock sourceBlock2, int batchSize, DataflowJoinOptions joinOptions = default(DataflowJoinOptions)); 80 | 81 | /// 82 | /// Creates a dataflow builder from a batched join of multiple source blocks. 83 | /// 84 | /// 85 | /// 86 | /// 87 | /// 88 | /// 89 | /// 90 | /// 91 | /// 92 | /// 93 | ISourceDataflowBuilder, IList, IList>> BatchedJoin(ISourceBlock sourceBlock1, ISourceBlock sourceBlock2, ISourceBlock sourceBlock3, int batchSize, DataflowJoinOptions joinOptions = default(DataflowJoinOptions)); 94 | 95 | /// 96 | /// Createsa dataflow builder from a broadcast block. 97 | /// 98 | /// 99 | /// 100 | /// 101 | IBroadcastDataflowBuilder FromBroadcast(BroadcastBlock broadcastBlock); 102 | 103 | /// 104 | /// Encapsulates custom dataflow block head and tail as one dataflow block. 105 | /// 106 | /// 107 | /// 108 | /// 109 | IDataflowBlock EncapsulateDataflow(IDataflowBlock head, IDataflowBlock tail); 110 | 111 | /// 112 | /// Encapsulates custom dataflow block multiple heads and a tail as one dataflow block. 113 | /// 114 | /// 115 | /// 116 | /// 117 | IDataflowBlock EncapsulateDataflow(IEnumerable multipleHeads, IDataflowBlock tail); 118 | 119 | /// 120 | /// Encapsulates custom dataflow block head and tail as one dataflow block. 121 | /// 122 | /// 123 | /// 124 | /// 125 | /// 126 | ISourceBlock EncapsulateSourceDataflow(IDataflowBlock head, ISourceBlock tail); 127 | 128 | /// 129 | /// Encapsulates custom dataflow block multiple heads and a tail as one dataflow block. 130 | /// 131 | /// 132 | /// 133 | /// 134 | /// 135 | ISourceBlock EncapsulateSourceDataflow(IEnumerable multipleHeads, ISourceBlock tail); 136 | 137 | /// 138 | /// Encapsulates custom dataflow block head and tail as one dataflow block. 139 | /// 140 | /// 141 | /// 142 | /// 143 | /// 144 | ITargetBlock EncapsulateTargetDataflow(ITargetBlock head, IDataflowBlock tail); 145 | 146 | /// 147 | /// Encapsulates custom dataflow block head and tail as one dataflow block. 148 | /// 149 | /// 150 | /// 151 | /// 152 | /// 153 | /// 154 | IPropagatorBlock EncapsulatePropagatorDataflow(ITargetBlock head, ISourceBlock tail); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /FluentDataflow/LinkHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks.Dataflow; 3 | 4 | namespace FluentDataflow 5 | { 6 | internal static class LinkHelper 7 | { 8 | public static void Link(ISourceBlock sourceBlock, ITargetBlock targetBlock, DataflowLinkOptions linkOptions, Predicate predicate = null) 9 | { 10 | // by default, propagate completion of source to target 11 | var options = linkOptions ?? DataflowDefaultOptions.DefaultLinkOptions; 12 | 13 | if (predicate == null) 14 | sourceBlock.LinkTo(targetBlock, options); 15 | else 16 | sourceBlock.LinkTo(targetBlock, options, predicate); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /FluentDataflow/MultipleSourceDataflowBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks.Dataflow; 2 | using System; 3 | 4 | namespace FluentDataflow 5 | { 6 | internal class MultipleSourceDataflowBuilder : IMultipleSourceDataflowBuilder 7 | { 8 | private readonly ISourceBlock[] _sourceBlocks; 9 | 10 | public ISourceBlock[] SourceBlocks => _sourceBlocks; 11 | 12 | public MultipleSourceDataflowBuilder(ISourceBlock[] sourceBlocks) 13 | { 14 | _sourceBlocks = sourceBlocks; 15 | } 16 | 17 | public ISourceDataflowBuilder Batch(int batchSize, DataflowBatchOptions batchOptions = default(DataflowBatchOptions)) 18 | { 19 | var batchBlock = new BatchBlock(batchSize, batchOptions.BatchBlockOptions); 20 | var propagateCompletion = Link(batchBlock, batchOptions.LinkOptions, null); 21 | var sourceWrapper = new MultipleSourceDataflowWrapper(_sourceBlocks); 22 | return new SourceDataflowBuilder(sourceWrapper, sourceWrapper, batchBlock, propagateCompletion); 23 | } 24 | 25 | public ISourceDataflowBuilder LinkToPropagator(IPropagatorBlock propagatorBlock, DataflowLinkOptions linkOptions, Predicate predicate) 26 | { 27 | if (propagatorBlock == null) throw new ArgumentNullException("propagatorBlock"); 28 | 29 | var propagateCompletion = Link(propagatorBlock, linkOptions, predicate); 30 | var sourceWrapper = new MultipleSourceDataflowWrapper(_sourceBlocks); 31 | return new SourceDataflowBuilder(sourceWrapper, sourceWrapper, propagatorBlock, propagateCompletion); 32 | } 33 | 34 | public IDataflowBuilder LinkToTarget(ITargetBlock targetBlock, DataflowLinkOptions linkOptions, Predicate predicate) 35 | { 36 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 37 | 38 | var propagateCompletion = Link(targetBlock, linkOptions, predicate); 39 | var sourceWrapper = new MultipleSourceDataflowWrapper(_sourceBlocks); 40 | return new DataflowBuilder(sourceWrapper, sourceWrapper, targetBlock, propagateCompletion); 41 | } 42 | 43 | public ISourceDataflowBuilder WriteOnce(Func cloningFunction, DataflowWriteOnceOptions writeOnceOptions = default(DataflowWriteOnceOptions)) 44 | { 45 | if (cloningFunction == null) throw new ArgumentNullException("cloningFunction"); 46 | 47 | var writeOnceBlock = new WriteOnceBlock(cloningFunction, writeOnceOptions.WriteOnceBlockOptions); 48 | var propagateCompletion = Link(writeOnceBlock, writeOnceOptions.LinkOptions, null); 49 | var sourceWrapper = new MultipleSourceDataflowWrapper(_sourceBlocks); 50 | return new SourceDataflowBuilder(sourceWrapper, sourceWrapper, writeOnceBlock, propagateCompletion); 51 | } 52 | 53 | internal bool Link(ITargetBlock targetBlock, DataflowLinkOptions linkOptions, Predicate predicate) 54 | { 55 | var options = linkOptions ?? DataflowDefaultOptions.DefaultLinkOptions; 56 | var propagateCompletion = options.PropagateCompletion; 57 | 58 | // when multiple source link to one target, 59 | // to make the PropagateCompletion behavior be consistent with when only one source 60 | // we need to disable it in LinkTo and hack it in MultipleDataflowWrapper. 61 | // so here we simply return the original PropagateCompletion value 62 | options.PropagateCompletion = false; 63 | foreach (var sourceBlock in _sourceBlocks) 64 | { 65 | if (predicate == null) 66 | sourceBlock.LinkTo(targetBlock, options); 67 | else 68 | sourceBlock.LinkTo(targetBlock, options, predicate); 69 | } 70 | 71 | return propagateCompletion; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /FluentDataflow/MultipleSourceDataflowWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace FluentDataflow 7 | { 8 | internal class MultipleSourceDataflowWrapper : IDataflowBlock 9 | { 10 | private readonly IDataflowBlock[] _sourceBlocks; 11 | 12 | public MultipleSourceDataflowWrapper(IDataflowBlock[] sourceBlocks) 13 | { 14 | _sourceBlocks = sourceBlocks; 15 | } 16 | 17 | public Task Completion => Task.WhenAll(_sourceBlocks.Select(s => s.Completion)); 18 | 19 | public void Complete() 20 | { 21 | foreach (var sourceBlock in _sourceBlocks) 22 | { 23 | sourceBlock.Complete(); 24 | } 25 | } 26 | 27 | public void Fault(Exception exception) 28 | { 29 | foreach (var sourceBlock in _sourceBlocks) 30 | { 31 | sourceBlock.Fault(exception); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /FluentDataflow/PropagatorDataflowBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks.Dataflow; 2 | using System; 3 | 4 | namespace FluentDataflow 5 | { 6 | internal class PropagatorDataflowBuilder : IPropagatorDataflowBuilder 7 | { 8 | private readonly ITargetBlock _originalTargetBlock; 9 | private readonly IDataflowBlock _currentSourceBlock; 10 | private readonly ISourceBlock _finalSourceBlock; 11 | private readonly bool? _propagateCompletion; 12 | 13 | public ITargetBlock OriginalTargetBlock => _originalTargetBlock; 14 | public IDataflowBlock CurrentSourceBlock => _currentSourceBlock; 15 | public ISourceBlock FinalSourceBlock => _finalSourceBlock; 16 | public bool? PropagateCompletion => _propagateCompletion; 17 | 18 | public PropagatorDataflowBuilder(IPropagatorBlock propagatorBlock) 19 | : this(propagatorBlock, propagatorBlock, propagatorBlock) 20 | { 21 | } 22 | 23 | public PropagatorDataflowBuilder( 24 | ITargetBlock originalTargetBlock 25 | , IDataflowBlock currentSourceBlock 26 | , ISourceBlock finalSourceBlock 27 | , bool? propagateCompletion = null) 28 | { 29 | _originalTargetBlock = originalTargetBlock; 30 | _currentSourceBlock = currentSourceBlock; 31 | _finalSourceBlock = finalSourceBlock; 32 | _propagateCompletion = propagateCompletion; 33 | } 34 | 35 | public IPropagatorBlock Create() 36 | { 37 | if (ReferenceEquals(_originalTargetBlock, _finalSourceBlock)) 38 | return _originalTargetBlock as IPropagatorBlock; 39 | if (ReferenceEquals(_originalTargetBlock, _currentSourceBlock)) 40 | return DataflowBlock.Encapsulate(_originalTargetBlock, _finalSourceBlock); 41 | 42 | if (_finalSourceBlock is IReceivableSourceBlock) 43 | return new ReceivablePropagatorDataflowWrapper(_originalTargetBlock, _currentSourceBlock, _finalSourceBlock, _propagateCompletion); 44 | else 45 | return new PropagatorDataflowWrapper(_originalTargetBlock, _currentSourceBlock, _finalSourceBlock, _propagateCompletion); 46 | } 47 | 48 | public ITargetDataflowBuilder LinkToTarget(ITargetBlock targetBlock, DataflowLinkOptions linkOptions, Predicate predicate) 49 | { 50 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 51 | 52 | LinkHelper.Link(_finalSourceBlock, targetBlock, linkOptions, predicate); 53 | return new TargetDataflowBuilder(_originalTargetBlock, targetBlock); 54 | } 55 | 56 | public IPropagatorDataflowBuilder LinkToPropagator(IPropagatorBlock propagatorBlock, DataflowLinkOptions linkOptions, Predicate predicate) 57 | { 58 | if (propagatorBlock == null) throw new ArgumentNullException("propagatorBlock"); 59 | 60 | LinkHelper.Link(_finalSourceBlock, propagatorBlock, linkOptions, predicate); 61 | return new PropagatorDataflowBuilder(_originalTargetBlock, _finalSourceBlock, propagatorBlock, _propagateCompletion); 62 | } 63 | 64 | public IPropagatorDataflowBuilder Batch(int batchSize, DataflowBatchOptions batchOptions = default(DataflowBatchOptions)) 65 | { 66 | var batchBlock = new BatchBlock(batchSize, batchOptions.BatchBlockOptions); 67 | LinkHelper.Link(_finalSourceBlock, batchBlock, batchOptions.LinkOptions); 68 | return new PropagatorDataflowBuilder(_originalTargetBlock, _finalSourceBlock, batchBlock, _propagateCompletion); 69 | } 70 | 71 | public IPropagatorDataflowBuilder WriteOnce(Func cloningFunction, DataflowWriteOnceOptions writeOnceOptions = default(DataflowWriteOnceOptions)) 72 | { 73 | if (cloningFunction == null) throw new ArgumentNullException("cloningFunction"); 74 | 75 | var writeOnceBlock = new WriteOnceBlock(cloningFunction, writeOnceOptions.WriteOnceBlockOptions); 76 | LinkHelper.Link(_finalSourceBlock, writeOnceBlock, writeOnceOptions.LinkOptions); 77 | return new PropagatorDataflowBuilder(_originalTargetBlock, _finalSourceBlock, writeOnceBlock, _propagateCompletion); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /FluentDataflow/PropagatorDataflowWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow 6 | { 7 | internal class PropagatorDataflowWrapper : IPropagatorBlock 8 | { 9 | private readonly ITargetBlock _originalTargetBlock; 10 | private readonly IDataflowBlock _currentSourceBlock; 11 | private readonly ISourceBlock _finalSourceBlock; 12 | private readonly bool? _propagateCompletion; 13 | 14 | public PropagatorDataflowWrapper(ITargetBlock originalTargetBlock 15 | , IDataflowBlock currentSourceBlock 16 | , ISourceBlock finalSourceBlock 17 | , bool? propagateCompletion = null) 18 | { 19 | _originalTargetBlock = originalTargetBlock; 20 | _currentSourceBlock = currentSourceBlock; 21 | _finalSourceBlock = finalSourceBlock; 22 | _propagateCompletion = propagateCompletion; 23 | } 24 | 25 | public Task Completion 26 | { 27 | get 28 | { 29 | if (_propagateCompletion.GetValueOrDefault()) 30 | { 31 | return _currentSourceBlock.Completion.ContinueWith(task => 32 | { 33 | if (task.IsFaulted) 34 | _finalSourceBlock.Fault(task.Exception); 35 | else 36 | _finalSourceBlock.Complete(); 37 | 38 | return _finalSourceBlock.Completion; 39 | }); 40 | } 41 | 42 | return _finalSourceBlock.Completion; 43 | } 44 | } 45 | 46 | public void Complete() 47 | { 48 | _originalTargetBlock.Complete(); 49 | } 50 | 51 | public TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) 52 | { 53 | return _finalSourceBlock.ConsumeMessage(messageHeader, target, out messageConsumed); 54 | } 55 | 56 | public void Fault(Exception exception) 57 | { 58 | _originalTargetBlock.Fault(exception); 59 | } 60 | 61 | public IDisposable LinkTo(ITargetBlock target, DataflowLinkOptions linkOptions) 62 | { 63 | return _finalSourceBlock.LinkTo(target, linkOptions); 64 | } 65 | 66 | public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) 67 | { 68 | return _originalTargetBlock.OfferMessage(messageHeader, messageValue, source, consumeToAccept); 69 | } 70 | 71 | public void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock target) 72 | { 73 | _finalSourceBlock.ReleaseReservation(messageHeader, target); 74 | } 75 | 76 | public bool ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock target) 77 | { 78 | return _finalSourceBlock.ReserveMessage(messageHeader, target); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /FluentDataflow/ReceivablePropagatorDataflowWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow 6 | { 7 | internal class ReceivablePropagatorDataflowWrapper : PropagatorDataflowWrapper, IReceivableSourceBlock 8 | { 9 | private readonly IReceivableSourceBlock _receivableSourceBlock; 10 | 11 | public ReceivablePropagatorDataflowWrapper(ITargetBlock originalTargetBlock 12 | , IDataflowBlock currentSourceBlock 13 | , ISourceBlock finalSourceBlock 14 | , bool? propagateCompletion = null) 15 | : base(originalTargetBlock, currentSourceBlock, finalSourceBlock, propagateCompletion) 16 | { 17 | _receivableSourceBlock = finalSourceBlock as IReceivableSourceBlock; 18 | } 19 | 20 | public bool TryReceive(Predicate filter, out TOutput item) 21 | { 22 | return _receivableSourceBlock.TryReceive(filter, out item); 23 | } 24 | 25 | public bool TryReceiveAll(out IList items) 26 | { 27 | return _receivableSourceBlock.TryReceiveAll(out items); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /FluentDataflow/ReceivableSourceDataflowWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow 6 | { 7 | internal class ReceivableSourceDataflowWrapper : SourceDataflowWrapper, IReceivableSourceBlock 8 | { 9 | private readonly IReceivableSourceBlock _receivableSourceBlock; 10 | 11 | public ReceivableSourceDataflowWrapper( 12 | IDataflowBlock originalSourceBlock 13 | , IDataflowBlock currentSourceBlock 14 | , ISourceBlock finalSourceBlock 15 | , bool? propagateCompletion = null) 16 | : base(originalSourceBlock, currentSourceBlock, finalSourceBlock, propagateCompletion) 17 | { 18 | _receivableSourceBlock = finalSourceBlock as IReceivableSourceBlock; 19 | } 20 | 21 | public bool TryReceive(Predicate filter, out TOutput item) 22 | { 23 | return _receivableSourceBlock.TryReceive(filter, out item); 24 | } 25 | 26 | public bool TryReceiveAll(out IList items) 27 | { 28 | return _receivableSourceBlock.TryReceiveAll(out items); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /FluentDataflow/SourceDataflowBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks.Dataflow; 2 | using System; 3 | 4 | namespace FluentDataflow 5 | { 6 | internal class SourceDataflowBuilder : ISourceDataflowBuilder 7 | { 8 | private readonly IDataflowBlock _originalSourceBlock; 9 | private readonly IDataflowBlock _currentSourceBlock; 10 | private readonly ISourceBlock _finalSourceBlock; 11 | private readonly bool? _propagateCompletion; 12 | 13 | public IDataflowBlock OriginalSourceBlock => _originalSourceBlock; 14 | public IDataflowBlock CurrentSourceBlock => _currentSourceBlock; 15 | public ISourceBlock FinalSourceBlock => _finalSourceBlock; 16 | public bool? PropagateCompletion => _propagateCompletion; 17 | 18 | public SourceDataflowBuilder(ISourceBlock sourceBlock) 19 | : this(sourceBlock, sourceBlock, sourceBlock) 20 | { 21 | } 22 | 23 | public SourceDataflowBuilder( 24 | IDataflowBlock originalSourceBlock 25 | , IDataflowBlock currentSourceBlock 26 | , ISourceBlock finalSourceBlock 27 | , bool? propagateCompletion = null) 28 | { 29 | _originalSourceBlock = originalSourceBlock; 30 | _currentSourceBlock = currentSourceBlock; 31 | _finalSourceBlock = finalSourceBlock; 32 | _propagateCompletion = propagateCompletion; 33 | } 34 | 35 | public ISourceBlock Create() 36 | { 37 | if (ReferenceEquals(_originalSourceBlock, _finalSourceBlock)) return _finalSourceBlock; 38 | 39 | if (_finalSourceBlock is IReceivableSourceBlock) 40 | return new ReceivableSourceDataflowWrapper(_originalSourceBlock, _currentSourceBlock, _finalSourceBlock, _propagateCompletion); 41 | else 42 | return new SourceDataflowWrapper(_originalSourceBlock, _currentSourceBlock, _finalSourceBlock, _propagateCompletion); 43 | } 44 | 45 | public IDataflowBuilder LinkToTarget(ITargetBlock targetBlock, DataflowLinkOptions linkOptions, Predicate predicate) 46 | { 47 | if (targetBlock == null) throw new ArgumentNullException("targetBlock"); 48 | 49 | LinkHelper.Link(_finalSourceBlock, targetBlock, linkOptions, predicate); 50 | return new DataflowBuilder(_originalSourceBlock, _finalSourceBlock, targetBlock, _propagateCompletion); 51 | } 52 | 53 | public ISourceDataflowBuilder LinkToPropagator(IPropagatorBlock propagatorBlock, DataflowLinkOptions linkOptions, Predicate predicate) 54 | { 55 | if (propagatorBlock == null) throw new ArgumentNullException("propagatorBlock"); 56 | 57 | LinkHelper.Link(_finalSourceBlock, propagatorBlock, linkOptions, predicate); 58 | return new SourceDataflowBuilder(_originalSourceBlock, _finalSourceBlock, propagatorBlock, _propagateCompletion); 59 | } 60 | 61 | public ISourceDataflowBuilder Batch(int batchSize, DataflowBatchOptions batchOptions) 62 | { 63 | var batchBlock = new BatchBlock(batchSize, batchOptions.BatchBlockOptions); 64 | LinkHelper.Link(_finalSourceBlock, batchBlock, batchOptions.LinkOptions); 65 | return new SourceDataflowBuilder(_originalSourceBlock, _finalSourceBlock, batchBlock, _propagateCompletion); 66 | } 67 | 68 | public ISourceDataflowBuilder WriteOnce(Func cloningFunction, DataflowWriteOnceOptions writeOnceOptions) 69 | { 70 | if (cloningFunction == null) throw new ArgumentNullException("cloningFunction"); 71 | 72 | var writeOnceBlock = new WriteOnceBlock(cloningFunction, writeOnceOptions.WriteOnceBlockOptions); 73 | LinkHelper.Link(_finalSourceBlock, writeOnceBlock, writeOnceOptions.LinkOptions); 74 | return new SourceDataflowBuilder(_originalSourceBlock, _finalSourceBlock, writeOnceBlock, _propagateCompletion); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /FluentDataflow/SourceDataflowWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow 6 | { 7 | internal class SourceDataflowWrapper : ISourceBlock 8 | { 9 | private readonly IDataflowBlock _originalSourceBlock; 10 | private readonly IDataflowBlock _currentSourceBlock; 11 | private readonly ISourceBlock _finalSourceBlock; 12 | private readonly bool? _propagateCompletion; 13 | 14 | public SourceDataflowWrapper( 15 | IDataflowBlock originalSourceBlock 16 | , IDataflowBlock currentSourceBlock 17 | , ISourceBlock finalSourceBlock 18 | , bool? propagateCompletion = null) 19 | { 20 | _originalSourceBlock = originalSourceBlock; 21 | _currentSourceBlock = currentSourceBlock; 22 | _finalSourceBlock = finalSourceBlock; 23 | _propagateCompletion = propagateCompletion; 24 | } 25 | 26 | public Task Completion 27 | { 28 | get 29 | { 30 | if (_propagateCompletion.GetValueOrDefault()) 31 | { 32 | return _currentSourceBlock.Completion.ContinueWith(task => 33 | { 34 | if (task.IsFaulted) 35 | _finalSourceBlock.Fault(task.Exception); 36 | else 37 | _finalSourceBlock.Complete(); 38 | 39 | return _finalSourceBlock.Completion; 40 | }); 41 | } 42 | 43 | return _finalSourceBlock.Completion; 44 | } 45 | } 46 | 47 | public void Complete() 48 | { 49 | _originalSourceBlock.Complete(); 50 | } 51 | 52 | public TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) 53 | { 54 | return _finalSourceBlock.ConsumeMessage(messageHeader, target, out messageConsumed); 55 | } 56 | 57 | public void Fault(Exception exception) 58 | { 59 | _originalSourceBlock.Fault(exception); 60 | } 61 | 62 | public IDisposable LinkTo(ITargetBlock target, DataflowLinkOptions linkOptions) 63 | { 64 | return _finalSourceBlock.LinkTo(target, linkOptions); 65 | } 66 | 67 | public void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock target) 68 | { 69 | _finalSourceBlock.ReleaseReservation(messageHeader, target); 70 | } 71 | 72 | public bool ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock target) 73 | { 74 | return _finalSourceBlock.ReserveMessage(messageHeader, target); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /FluentDataflow/TargetDataflowBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks.Dataflow; 2 | 3 | namespace FluentDataflow 4 | { 5 | internal class TargetDataflowBuilder : ITargetDataflowBuilder 6 | { 7 | private readonly ITargetBlock _originalTargetBlock; 8 | private readonly IDataflowBlock _finalTargetBlock; 9 | 10 | public ITargetBlock OriginalTargetBlock => _originalTargetBlock; 11 | public IDataflowBlock FinalTargetBlock => _finalTargetBlock; 12 | 13 | public TargetDataflowBuilder(ITargetBlock targetBlock) 14 | : this(targetBlock, targetBlock) 15 | { 16 | } 17 | 18 | public TargetDataflowBuilder(ITargetBlock originalTargetBlock, IDataflowBlock finalTargetBlock) 19 | { 20 | _originalTargetBlock = originalTargetBlock; 21 | _finalTargetBlock = finalTargetBlock; 22 | } 23 | 24 | public ITargetBlock Create() 25 | { 26 | if (ReferenceEquals(_originalTargetBlock, _finalTargetBlock)) return _originalTargetBlock; 27 | 28 | return new TargetDataflowWrapper(_originalTargetBlock, _finalTargetBlock); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /FluentDataflow/TargetDataflowWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace FluentDataflow 6 | { 7 | internal class TargetDataflowWrapper : ITargetBlock 8 | { 9 | private readonly ITargetBlock _originalTargetBlock; 10 | private readonly IDataflowBlock _finalTargetBlock; 11 | 12 | public TargetDataflowWrapper(ITargetBlock originalTargetBlock, IDataflowBlock finalTargetBlock) 13 | { 14 | _originalTargetBlock = originalTargetBlock; 15 | _finalTargetBlock = finalTargetBlock; 16 | } 17 | 18 | public Task Completion => _finalTargetBlock.Completion; 19 | 20 | public void Complete() 21 | { 22 | _originalTargetBlock.Complete(); 23 | } 24 | 25 | public void Fault(Exception exception) 26 | { 27 | _originalTargetBlock.Fault(exception); 28 | } 29 | 30 | public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) 31 | { 32 | return _originalTargetBlock.OfferMessage(messageHeader, messageValue, source, consumeToAccept); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /FluentDataflowPortable/FluentDataflowPortable.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24} 9 | Library 10 | Properties 11 | FluentDataflow 12 | FluentDataflow 13 | v4.5 14 | Profile259 15 | 512 16 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 17 | 18 | 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | bin\Debug\FluentDataflow.XML 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | bin\Release\FluentDataflow.XML 38 | 39 | 40 | 41 | BroadcastDataflowBuilder.cs 42 | 43 | 44 | BroadcastDataflowWrapper.cs 45 | 46 | 47 | DataflowBatchOptions.cs 48 | 49 | 50 | DataflowBuilder.cs 51 | 52 | 53 | DataflowBuilderExtenions.cs 54 | 55 | 56 | DataflowDefaultOptions.cs 57 | 58 | 59 | DataflowFactory.cs 60 | 61 | 62 | DataflowFactoryExtensions.cs 63 | 64 | 65 | DataflowJoinOptions.cs 66 | 67 | 68 | DataflowOptionsExtensions.cs 69 | 70 | 71 | DataflowWrapper.cs 72 | 73 | 74 | DataflowWriteOnceOptions.cs 75 | 76 | 77 | IDataflowBuilder.cs 78 | 79 | 80 | IDataflowFactory.cs 81 | 82 | 83 | LinkHelper.cs 84 | 85 | 86 | MultipleSourceDataflowBuilder.cs 87 | 88 | 89 | MultipleSourceDataflowWrapper.cs 90 | 91 | 92 | PropagatorDataflowBuilder.cs 93 | 94 | 95 | PropagatorDataflowWrapper.cs 96 | 97 | 98 | ReceivablePropagatorDataflowWrapper.cs 99 | 100 | 101 | ReceivableSourceDataflowWrapper.cs 102 | 103 | 104 | SourceDataflowBuilder.cs 105 | 106 | 107 | SourceDataflowWrapper.cs 108 | 109 | 110 | TargetDataflowBuilder.cs 111 | 112 | 113 | TargetDataflowWrapper.cs 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | FluentDataflow.csproj 123 | 124 | 125 | 126 | 127 | ..\packages\System.Threading.Tasks.Dataflow.4.8.0\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll 128 | 129 | 130 | 131 | 132 | 133 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 134 | 135 | 136 | 143 | -------------------------------------------------------------------------------- /FluentDataflowPortable/FluentDataflowPortable.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BFCDCA69-55BB-4224-9E90-9674E4439544}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\README.md = ..\README.md 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentDataflowPortable", "FluentDataflowPortable.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {112A5B40-8933-4453-A2C9-9BE90A0BB048} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /FluentDataflowPortable/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("FluentDataflow")] 10 | [assembly: AssemblyDescription("A fluent style wrapper and extension of TPL Dataflow.")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("FluentDataflow")] 14 | [assembly: AssemblyCopyright("Teddy Ma")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: NeutralResourcesLanguage("en")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.7.0")] 30 | [assembly: AssemblyFileVersion("1.0.7.0")] 31 | -------------------------------------------------------------------------------- /FluentDataflowPortable/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FluentDataflow 2 | ============== 3 | 4 | FluentDataflow is a fluent style wrapper and extension of TPL Dataflow. 5 | 6 | License terms 7 | ------------- 8 | FluentDataflow is released under the [MIT license](https://mit-license.org/). 9 | 10 | Nuget Package 11 | ------------- 12 | 13 | - [FluentDataflow](https://www.nuget.org/packages/FluentDataflow/) 14 | 15 | Examples 16 | -------- 17 | You can git clone this repo and run [the test app](https://github.com/teddymacn/FluentDataflow/tree/master/FluentDataflow.Tests.Console) for real examples. 18 | 19 | - Simple aggregation example: 20 | 21 | ``` 22 | var splitter = new TransformBlock>(input => 23 | { 24 | string[] splitted = input.Split('='); 25 | return new KeyValuePair(splitted[0], int.Parse(splitted[1])); 26 | }); 27 | 28 | var dict = new Dictionary(); 29 | 30 | var aggregater = new ActionBlock>(pair => 31 | { 32 | int oldValue; 33 | dict[pair.Key] = dict.TryGetValue(pair.Key, out oldValue) ? oldValue + pair.Value : pair.Value; 34 | }); 35 | 36 | // the created the dataflow instance is also 37 | // a strong-typed standard dataflow block 38 | var dataflow = new DataflowFactory().FromPropagator(splitter) 39 | .LinkToTarget(aggregater) 40 | .Create(); 41 | 42 | dataflow.Post("a=1"); 43 | dataflow.Post("b=2"); 44 | dataflow.Post("a=5"); 45 | dataflow.Complete(); 46 | 47 | Task.WaitAll(dataflow.Completion); 48 | 49 | System.Console.WriteLine("sum(a) = {0}", dict["a"]); //prints sum(a) = 6 50 | ``` 51 | 52 | - A dataflow as part of another dataflow example 53 | 54 | ``` 55 | var factory = new DataflowFactory(); 56 | 57 | var aggregater = ... // Build an aggregator dataflow 58 | var splitter = new TransformManyBlock(line => line.Split(' ')); 59 | 60 | var dataflow = factory.FromPropagator(splitter) 61 | .LinkToTarget(aggregator) 62 | .Create(); 63 | 64 | dataflow.Post("a=1 b=2 a=5"); 65 | dataflow.Post("c=6 b=8"); 66 | dataflow.Complete(); 67 | 68 | Task.WaitAll(dataflow.Completion); 69 | 70 | System.Console.WriteLine("sum(b) = {0}", result["b"]); //prints sum(b) = 10 71 | ``` 72 | 73 | - Broadcast example 74 | 75 | ``` 76 | var printer1 = new ActionBlock(s => System.Console.WriteLine("Printer1: {0}", s)); 77 | var printer2 = new ActionBlock(s => System.Console.WriteLine("Printer2: {0}", s)); 78 | var printer3 = new ActionBlock(s => System.Console.WriteLine("Printer3: {0}", s)); 79 | 80 | var dataflow = new DataflowFactory().FromBroadcast() 81 | .LinkTo(printer1) 82 | .LinkTo(printer2) 83 | .LinkTo(printer3) 84 | .Create(); 85 | 86 | dataflow.Post("first message"); 87 | dataflow.Post("second message"); 88 | dataflow.Post("third message"); 89 | dataflow.Complete(); 90 | 91 | Task.WaitAll(dataflow.Completion); 92 | ``` 93 | 94 | - Multiple sources example 95 | 96 | ``` 97 | // by default, with native TPL, if multiple sources link to the same target, 98 | // if set PropagateCompletion=true, 99 | // as long as one of the source complets, the target complets. 100 | // the target will miss some of the messages from the other sources. 101 | 102 | // BUT, with our dataflow factory here, when set PropagateCompletion=true, 103 | // dataflow.Complete() internally waits for all the sources to complete, 104 | // so the target is guaranteed to receive all the messages from all the sources 105 | 106 | var source1 = new BufferBlock(); 107 | var source2 = new BufferBlock(); 108 | var source3 = new BufferBlock(); 109 | var printer = new ActionBlock(s => System.Console.WriteLine(s)); 110 | 111 | var dataflow = new DataflowFactory().FromMultipleSources(source1, source2, source3) 112 | .LinkToTarget(printer) 113 | .Create(); 114 | 115 | for (var i = 0; i < 3; ++i) 116 | { 117 | var s = i.ToString(); 118 | source1.Post(s); 119 | source2.Post(s); 120 | source3.Post(s); 121 | } 122 | 123 | dataflow.Complete(); 124 | 125 | Task.WaitAll(dataflow.Completion); 126 | ``` 127 | 128 | - Linking with filter example 129 | 130 | ``` 131 | // the filter only accepts even numbers, 132 | // so odd numbers goes to declined printer 133 | var filter = new Predicate(i => 134 | { 135 | return i % 2 == 0; 136 | }); 137 | var printer = new ActionBlock(s => System.Console.WriteLine("printer: " + s.ToString())); 138 | var declinedPrinter = new ActionBlock(s => System.Console.WriteLine("declined: " + s.ToString())); 139 | var inputBlock = new BufferBlock(); 140 | 141 | var dataflow = new DataflowFactory().FromPropagator(inputBlock) 142 | .LinkToTarget(printer 143 | , filter 144 | // when linking with filter, you have to specify a declined block 145 | // otherwise, because there will be messages declined still in the queue, 146 | // the current block will not be able to COMPLETE (waits on its Completion will never return) 147 | , declinedPrinter) 148 | .Create(); 149 | 150 | for (int i = 0; i < 10; ++i) 151 | { 152 | dataflow.Post(i); 153 | } 154 | 155 | dataflow.Complete(); 156 | 157 | Task.WaitAll(dataflow.Completion); 158 | ``` 159 | - Join example 160 | 161 | ``` 162 | var source1 = new BufferBlock(); 163 | var source2 = new BufferBlock(); 164 | var printer = new ActionBlock>(s => System.Console.WriteLine("printer: {0},{1}", s.Item1, s.Item2)); 165 | 166 | var dataflow = new DataflowFactory().Join(source1, source2) 167 | .LinkToTarget(printer) 168 | .Create(); 169 | 170 | for (var i = 0; i < 3; ++i) 171 | { 172 | var s = i.ToString(); 173 | source1.Post(s); 174 | source2.Post(s); 175 | } 176 | 177 | dataflow.Complete(); 178 | 179 | Task.WaitAll(dataflow.Completion); 180 | ``` 181 | 182 | - Batch example 183 | 184 | ``` 185 | var source = new BufferBlock(); 186 | var printer = new ActionBlock>(s => System.Console.WriteLine("printer: " +string.Join("|", s))); 187 | 188 | var dataflow = new DataflowFactory().FromSource(source) 189 | .Batch(2) 190 | .LinkToTarget(printer) 191 | .Create(); 192 | 193 | for (var i = 0; i < 6; ++i) 194 | { 195 | var s = i.ToString(); 196 | source.Post(s); 197 | } 198 | 199 | dataflow.Complete(); 200 | 201 | Task.WaitAll(dataflow.Completion); 202 | ``` 203 | 204 | - If-else branching and merging example 205 | 206 | ``` 207 | var factory = new DataflowFactory(); 208 | // the ifFilter only accepts even numbers, 209 | // so odd numbers goes to elseBlock 210 | var ifFilter = new Predicate(i => 211 | { 212 | return i % 2 == 0; 213 | }); 214 | var printer = new ActionBlock(s => System.Console.WriteLine("printer: " + s.ToString())); 215 | var inputBlock = new BufferBlock(); 216 | // if meet ifFilter, convert to: i -> i * 10 217 | var ifBlock = new TransformBlock(i => i * 10); 218 | // else, convert to: i -> i * 100 219 | var elseBlock = new TransformBlock(i => i * 100); 220 | 221 | var branchingDataflow = factory.FromPropagator(inputBlock) 222 | .LinkToPropagator(ifBlock, ifFilter, elseBlock) 223 | .Create(); 224 | 225 | var mergeingDataflow = factory.FromMultipleSources(ifBlock, elseBlock) 226 | .LinkToTarget(printer) 227 | .Create(); 228 | 229 | //encapsulate branchingDataflow and mergeingDataflow 230 | var dataflow = factory.EncapsulateTargetDataflow(branchingDataflow, mergeingDataflow); 231 | 232 | for (int i = 0; i < 10; ++i) 233 | { 234 | dataflow.Post(i); 235 | } 236 | 237 | dataflow.Complete(); 238 | 239 | Task.WaitAll(dataflow.Completion); 240 | ``` 241 | 242 | - Fluent dataflow options creation 243 | 244 | ``` 245 | var joinOptions = new DataflowJoinOptions( 246 | join => join.BoundedCapacity(11).EnsureOrdered().MaxNumberOfGroups(1) 247 | , target2: _ => _.Append().MaxMessages(1).PropagateCompletion()); 248 | 249 | var batchOptions = new DataflowBatchOptions( 250 | batch => batch.BoundedCapacity(2).Greedy().MaxNumberOfGroups(3) 251 | , link => link.MaxMessages(1).PropagateCompletion(false) 252 | ); 253 | 254 | var linkOptions = new DataflowLinkOptions().PropagateCompletion(false).MaxMessages(2); 255 | ``` 256 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | #echo off 2 | dotnet build -c Release FluentDataFlow 3 | &"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" FluentDataFlowPortable\FluentDataFlowPortable.csproj /p:Configuration=Release 4 | -------------------------------------------------------------------------------- /pack.ps1: -------------------------------------------------------------------------------- 1 | #echo off 2 | del *.nupkg 3 | .\.nuget\NuGet.exe pack .\FluentDataflow\FluentDataflow.nuspec -Prop Configuration=Release -Sym 4 | -------------------------------------------------------------------------------- /push.ps1: -------------------------------------------------------------------------------- 1 | .nuget\nuget push *.symbols.nupkg -Source http://nuget.smbsrc.net/ 2 | del *.symbols.nupkg 3 | .nuget\nuget push *.nupkg -Source https://www.nuget.org 4 | --------------------------------------------------------------------------------