Skip to content

Commit 3299d51

Browse files
authored
fix: keep examples compiling in CI (#347)
Refs #341
1 parent 022bda4 commit 3299d51

4 files changed

Lines changed: 52 additions & 17 deletions

File tree

EnumerableAsyncProcessor.Example/DisposalExample.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#if NET6_0_OR_GREATER
22
using System;
33
using System.Collections.Generic;
4-
using System.Linq;
54
using System.Runtime.CompilerServices;
65
using System.Threading;
76
using System.Threading.Tasks;
@@ -29,19 +28,19 @@ public static async Task RunExamples()
2928
// Example 1: The problematic pattern from the issue
3029
Console.WriteLine("Example 1: PROBLEMATIC - No disposal (resource leak!)");
3130
var results1 = await ProblematicPatternAsync(new[] { 1, 2, 3, 4, 5 }, CancellationToken.None);
32-
Console.WriteLine($"Results: {string.Join(", ", results1.ToList())}");
31+
Console.WriteLine($"Results: {string.Join(", ", results1)}");
3332
Console.WriteLine("⚠️ This pattern leaks resources because the processor is never disposed!\n");
3433

3534
// Example 2: Proper disposal with await using
3635
Console.WriteLine("Example 2: PROPER - Using await using for automatic disposal");
3736
var results2 = await ProperPatternWithAwaitUsingAsync(new[] { 1, 2, 3, 4, 5 }, CancellationToken.None);
38-
Console.WriteLine($"Results: {string.Join(", ", results2.ToList())}");
37+
Console.WriteLine($"Results: {string.Join(", ", results2)}");
3938
Console.WriteLine("✅ Resources automatically cleaned up with await using\n");
4039

4140
// Example 3: Proper disposal with manual try-finally
4241
Console.WriteLine("Example 3: PROPER - Manual disposal with try-finally");
4342
var results3 = await ProperPatternWithManualDisposalAsync(new[] { 1, 2, 3, 4, 5 }, CancellationToken.None);
44-
Console.WriteLine($"Results: {string.Join(", ", results3.ToList())}");
43+
Console.WriteLine($"Results: {string.Join(", ", results3)}");
4544
Console.WriteLine("✅ Resources manually cleaned up in finally block\n");
4645

4746
// Example 4: Using the convenience extension (no disposal needed)
@@ -65,23 +64,27 @@ public static async Task RunExamples()
6564
/// This is the PROBLEMATIC pattern from the GitHub issue - it leaks resources!
6665
/// DO NOT USE THIS PATTERN in production code.
6766
/// </summary>
68-
private static async Task<IAsyncEnumerable<int>> ProblematicPatternAsync(int[] input, CancellationToken token)
67+
private static async Task<IReadOnlyList<int>> ProblematicPatternAsync(int[] input, CancellationToken token)
6968
{
7069
// ⚠️ PROBLEM: The processor is created but never disposed!
7170
var batchProcessor = input.SelectAsync(static v => TransformAsync(v), token).ProcessInParallel();
72-
73-
// This returns the async enumerable, but the processor that created it is never disposed
74-
return batchProcessor.GetResultsAsyncEnumerable();
75-
71+
72+
var results = new List<int>();
73+
await foreach (var result in batchProcessor.GetResultsAsyncEnumerable())
74+
{
75+
results.Add(result);
76+
}
77+
7678
// 🔥 RESOURCE LEAK: The processor goes out of scope without being disposed,
7779
// potentially leaving tasks running and resources uncleaned
80+
return results;
7881
}
7982

8083
/// <summary>
8184
/// PROPER pattern using await using for automatic disposal.
8285
/// This is the recommended approach.
8386
/// </summary>
84-
private static async Task<IAsyncEnumerable<int>> ProperPatternWithAwaitUsingAsync(int[] input, CancellationToken token)
87+
private static async Task<IReadOnlyList<int>> ProperPatternWithAwaitUsingAsync(int[] input, CancellationToken token)
8588
{
8689
// ✅ Create processor with await using for automatic disposal
8790
await using var processor = input.SelectAsync(static v => TransformAsync(v), token).ProcessInParallel();
@@ -93,17 +96,15 @@ private static async Task<IAsyncEnumerable<int>> ProperPatternWithAwaitUsingAsyn
9396
results.Add(result);
9497
}
9598

96-
// Return as async enumerable
97-
return results.ToAsyncEnumerable();
98-
9999
// ✅ Processor is automatically disposed here due to 'await using'
100+
return results;
100101
}
101102

102103
/// <summary>
103104
/// PROPER pattern using manual disposal with try-finally.
104105
/// Use this when you need more control over the disposal timing.
105106
/// </summary>
106-
private static async Task<IAsyncEnumerable<int>> ProperPatternWithManualDisposalAsync(int[] input, CancellationToken token)
107+
private static async Task<IReadOnlyList<int>> ProperPatternWithManualDisposalAsync(int[] input, CancellationToken token)
107108
{
108109
var processor = input.SelectAsync(static v => TransformAsync(v), token).ProcessInParallel();
109110

@@ -116,7 +117,7 @@ private static async Task<IAsyncEnumerable<int>> ProperPatternWithManualDisposal
116117
results.Add(result);
117118
}
118119

119-
return results.ToAsyncEnumerable();
120+
return results;
120121
}
121122
finally
122123
{
@@ -168,4 +169,4 @@ private static async IAsyncEnumerable<int> GenerateAsyncEnumerable(
168169
}
169170
}
170171
}
171-
#endif
172+
#endif
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using ModularPipelines.Context;
2+
using ModularPipelines.DotNet.Extensions;
3+
using ModularPipelines.DotNet.Options;
4+
using ModularPipelines.Git.Extensions;
5+
using ModularPipelines.Models;
6+
using ModularPipelines.Modules;
7+
8+
namespace EnumerableAsyncProcessor.Pipeline.Modules;
9+
10+
public class BuildExampleProjectsModule : Module<List<CommandResult>>
11+
{
12+
protected override async Task<List<CommandResult>?> ExecuteAsync(
13+
IModuleContext context,
14+
CancellationToken cancellationToken)
15+
{
16+
var results = new List<CommandResult>();
17+
18+
foreach (var exampleProjectFile in context
19+
.Git().RootDirectory
20+
.GetFiles(file => file.Path.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase)
21+
&& file.Path.Contains("Example", StringComparison.OrdinalIgnoreCase)))
22+
{
23+
results.Add(await context.DotNet().Build(new DotNetBuildOptions
24+
{
25+
ProjectSolution = exampleProjectFile.Path,
26+
Configuration = "Release",
27+
}, cancellationToken: cancellationToken));
28+
}
29+
30+
return results;
31+
}
32+
}

EnumerableAsyncProcessor.Pipeline/Modules/PackProjectsModule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace EnumerableAsyncProcessor.Pipeline.Modules;
1313
[DependsOn<PackageFilesRemovalModule>]
1414
[DependsOn<NugetVersionGeneratorModule>]
1515
[DependsOn<RunUnitTestsModule>]
16+
[DependsOn<BuildExampleProjectsModule>]
1617
public class PackProjectsModule : Module<List<CommandResult>>
1718
{
1819
protected override async Task<List<CommandResult>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)

EnumerableAsyncProcessor.Pipeline/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
builder.AddModule<UploadPackagesToNugetModule>();
2727
}
2828

29-
builder.AddModule<RunUnitTestsModule>()
29+
builder.AddModule<BuildExampleProjectsModule>()
30+
.AddModule<RunUnitTestsModule>()
3031
.AddModule<NugetVersionGeneratorModule>()
3132
.AddModule<PackProjectsModule>()
3233
.AddModule<PackageFilesRemovalModule>()

0 commit comments

Comments
 (0)