diff --git a/.gitignore b/.gitignore index 6c90e3a..84a0ed7 100644 --- a/.gitignore +++ b/.gitignore @@ -433,3 +433,4 @@ docfx_project/obj/ docs/* !docs/.gitkeep !docs/RELEASE-WORKFLOW-SETUP.md +.nuget/nuget.exe diff --git a/examples/Net8.0/Example6-ReducingDuplicateCode/ConsoleColors.cs b/examples/Net8.0/Example6-ReducingDuplicateCode/ConsoleColors.cs new file mode 100644 index 0000000..fd51374 --- /dev/null +++ b/examples/Net8.0/Example6-ReducingDuplicateCode/ConsoleColors.cs @@ -0,0 +1,11 @@ +namespace Example6_ReducingDuplicateCode +{ + internal class ConsoleColors + { + public const string Green = "\u001b[32m"; + public const string Yellow = "\u001b[33m"; + public const string Reset = "\u001b[0m"; + public const string Red = "\u001b[31m"; + public const string Cyan = "\u001b[36m"; + } +} diff --git a/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/ConsoleLoader.cs b/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/ConsoleLoader.cs index bdc98e8..045e599 100644 --- a/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/ConsoleLoader.cs +++ b/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/ConsoleLoader.cs @@ -28,9 +28,9 @@ public int ProgressInterval public Task LoadAsync(IAsyncEnumerable items) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); + ArgumentNullException.ThrowIfNull(items); - return WorkerAsync(items, null, CancellationToken.None); + return WorkerAsync(items, progress: null, CancellationToken.None); } @@ -38,17 +38,17 @@ public Task LoadAsync(IAsyncEnumerable items) public Task LoadAsync(IAsyncEnumerable items, CancellationToken token) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); + ArgumentNullException.ThrowIfNull(items); - return WorkerAsync(items, null, token); + return WorkerAsync(items, progress: null, token); } public Task LoadAsync(IAsyncEnumerable items, IProgress progress) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); - ArgumentNullException.ThrowIfNull(progress, nameof(progress)); + ArgumentNullException.ThrowIfNull(items); + ArgumentNullException.ThrowIfNull(progress); return WorkerAsync(items, progress, CancellationToken.None); } @@ -57,8 +57,8 @@ public Task LoadAsync(IAsyncEnumerable items, IProgress pro public Task LoadAsync(IAsyncEnumerable items, IProgress progress, CancellationToken token) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); - ArgumentNullException.ThrowIfNull(progress, nameof(progress)); + ArgumentNullException.ThrowIfNull(items); + ArgumentNullException.ThrowIfNull(progress); return WorkerAsync(items, progress, token); } @@ -72,7 +72,7 @@ private async Task WorkerAsync CancellationToken token ) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); + ArgumentNullException.ThrowIfNull(items); Console.WriteLine($"{ConsoleColors.Green}Loading{ConsoleColors.Reset} data to console asynchronously...\n"); @@ -80,10 +80,10 @@ CancellationToken token await using var timer = new Timer ( _ => progress?.Report(new EtlProgress(Volatile.Read(ref count))), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(_progressInterval) // Use the configured progress interval - ); + ).ConfigureAwait(false); await foreach (var item in items.WithCancellation(token)) @@ -91,7 +91,7 @@ CancellationToken token token.ThrowIfCancellationRequested(); Console.WriteLine($"Loading item: {item}\n"); - await Task.Delay(50); // Simulate some delay for loading + await Task.Delay(50, token).ConfigureAwait(false); // Simulate some delay for loading count = Interlocked.Increment(ref count); } diff --git a/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/FibonacciExtractor.cs b/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/FibonacciExtractor.cs index 9550590..e3274ee 100644 --- a/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/FibonacciExtractor.cs +++ b/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/FibonacciExtractor.cs @@ -28,21 +28,21 @@ public int ProgressInterval public IAsyncEnumerable ExtractAsync() { - return WorkerAsync(null, CancellationToken.None); + return WorkerAsync(progress: null, CancellationToken.None); } public IAsyncEnumerable ExtractAsync(CancellationToken token) { - return WorkerAsync(null, token); + return WorkerAsync(progress: null, token); } public IAsyncEnumerable ExtractAsync(IProgress progress) { - ArgumentNullException.ThrowIfNull(progress, nameof(progress)); + ArgumentNullException.ThrowIfNull(progress); return WorkerAsync(progress, CancellationToken.None); } @@ -55,7 +55,7 @@ public IAsyncEnumerable ExtractAsync CancellationToken token ) { - ArgumentNullException.ThrowIfNull(progress, nameof(progress)); + ArgumentNullException.ThrowIfNull(progress); return WorkerAsync(progress, token); } @@ -74,10 +74,10 @@ [EnumeratorCancellation] CancellationToken token await using var timer = new Timer ( _ => progress?.Report(new EtlProgress(Volatile.Read(ref count))), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(_progressInterval) // Use the configured progress interval - ); + ).ConfigureAwait(false); var current = 1; var previous = 0; @@ -100,7 +100,7 @@ [EnumeratorCancellation] CancellationToken token var temp = current; current += previous; previous = temp; - await Task.Delay(100); // Simulate asynchronous operation + await Task.Delay(100, CancellationToken.None).ConfigureAwait(false); // Simulate asynchronous operation } progress?.Report(new EtlProgress(Volatile.Read(ref count))); // Report final count diff --git a/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/IntToStringTransformer.cs b/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/IntToStringTransformer.cs index 24c7f7c..aa7e22b 100644 --- a/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/IntToStringTransformer.cs +++ b/examples/Net8.0/Example6-ReducingDuplicateCode/ETL/IntToStringTransformer.cs @@ -31,9 +31,9 @@ public IAsyncEnumerable TransformAsync IAsyncEnumerable items ) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); + ArgumentNullException.ThrowIfNull(items); - return WorkerAsync(items, null, CancellationToken.None); + return WorkerAsync(items, progress: null, CancellationToken.None); } @@ -44,9 +44,9 @@ public IAsyncEnumerable TransformAsync CancellationToken token ) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); + ArgumentNullException.ThrowIfNull(items); - return WorkerAsync(items, null, token); + return WorkerAsync(items, progress: null, token); } @@ -57,8 +57,8 @@ public IAsyncEnumerable TransformAsync IProgress progress ) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); - ArgumentNullException.ThrowIfNull(progress, nameof(progress)); + ArgumentNullException.ThrowIfNull(items); + ArgumentNullException.ThrowIfNull(progress); return WorkerAsync(items, progress, CancellationToken.None); } @@ -72,8 +72,8 @@ public IAsyncEnumerable TransformAsync CancellationToken token ) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); - ArgumentNullException.ThrowIfNull(progress, nameof(progress)); + ArgumentNullException.ThrowIfNull(items); + ArgumentNullException.ThrowIfNull(progress); return WorkerAsync(items, progress, token); @@ -88,7 +88,7 @@ private async IAsyncEnumerable WorkerAsync [EnumeratorCancellation] CancellationToken token ) { - ArgumentNullException.ThrowIfNull(items, nameof(items)); + ArgumentNullException.ThrowIfNull(items); Console.WriteLine($"{ConsoleColors.Green}Transforming{ConsoleColors.Reset} integers to strings asynchronously...\n"); @@ -96,10 +96,10 @@ [EnumeratorCancellation] CancellationToken token await using var timer = new Timer ( _ => progress?.Report(new EtlProgress(Volatile.Read(ref count))), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(_progressInterval) // Use the configured progress interval - ); + ).ConfigureAwait(false); await foreach (var item in items.WithCancellation(token)) @@ -115,7 +115,7 @@ [EnumeratorCancellation] CancellationToken token } Console.WriteLine($"Transforming integer {item} to string."); - await Task.Delay(50); // Simulate some delay for transformation + await Task.Delay(50, CancellationToken.None).ConfigureAwait(false); // Simulate some delay for transformation yield return item.ToString(); count = Interlocked.Increment(ref count); diff --git a/examples/Net8.0/Example6-ReducingDuplicateCode/EtlProgress.cs b/examples/Net8.0/Example6-ReducingDuplicateCode/EtlProgress.cs new file mode 100644 index 0000000..17708f1 --- /dev/null +++ b/examples/Net8.0/Example6-ReducingDuplicateCode/EtlProgress.cs @@ -0,0 +1,4 @@ +namespace Example6_ReducingDuplicateCode +{ + internal record EtlProgress(int CurrentCount); +} diff --git a/examples/Net8.0/Example6-ReducingDuplicateCode/Program.cs b/examples/Net8.0/Example6-ReducingDuplicateCode/Program.cs index f5f931d..26cabb7 100644 --- a/examples/Net8.0/Example6-ReducingDuplicateCode/Program.cs +++ b/examples/Net8.0/Example6-ReducingDuplicateCode/Program.cs @@ -16,16 +16,16 @@ private static async Task Main() Console.WriteLine($"{ConsoleColors.Green}.NET Version: {frameworkVersion}{ConsoleColors.Reset}\n"); - await EtlWithNoProgressOrCancellation(); + await EtlWithNoProgressOrCancellation().ConfigureAwait(false); - await EtlWithCancellationToken(); + await EtlWithCancellationToken().ConfigureAwait(false); - await EtlWithProgress(); + await EtlWithProgress().ConfigureAwait(false); - await EtlWithProgressAndCancellationToken(); + await EtlWithProgressAndCancellationToken().ConfigureAwait(false); Console.WriteLine($"\n\n{ConsoleColors.Yellow}All ETLs completed.{ConsoleColors.Reset}"); @@ -43,7 +43,7 @@ private static async Task EtlWithNoProgressOrCancellation() var sourceItems = extractor.ExtractAsync(); var transformedItems = transformer.TransformAsync(sourceItems); - await loader.LoadAsync(transformedItems); + await loader.LoadAsync(transformedItems).ConfigureAwait(false); Console.WriteLine($"\n\n{ConsoleColors.Yellow}ETL process completed.{ConsoleColors.Reset}"); @@ -67,7 +67,7 @@ private static async Task EtlWithCancellationToken() var sourceItems = extractor.ExtractAsync(token); var transformedItems = transformer.TransformAsync(sourceItems, token); - await loader.LoadAsync(transformedItems, token); + await loader.LoadAsync(transformedItems, token).ConfigureAwait(false); Console.WriteLine($"\n\n{ConsoleColors.Yellow}ETL process completed.{ConsoleColors.Reset}"); @@ -95,7 +95,7 @@ private static async Task EtlWithProgress() // the loader, but you could also pass it to the extractor or transformer depending on your needs. var sourceItems = extractor.ExtractAsync(); var transformedItems = transformer.TransformAsync(sourceItems); - await loader.LoadAsync(transformedItems, progress); + await loader.LoadAsync(transformedItems, progress).ConfigureAwait(false); Console.WriteLine($"\n\n{ConsoleColors.Yellow}ETL process completed.{ConsoleColors.Reset}"); @@ -126,25 +126,10 @@ private static async Task EtlWithProgressAndCancellationToken() // the loader, but you could also pass it to the extractor or transformer depending on your needs. var sourceItems = extractor.ExtractAsync(token); var transformedItems = transformer.TransformAsync(sourceItems, token); - await loader.LoadAsync(transformedItems, progress, token); + await loader.LoadAsync(transformedItems, progress, token).ConfigureAwait(false); Console.WriteLine($"\n\n{ConsoleColors.Yellow}ETL process completed.{ConsoleColors.Reset}"); } } - - - - internal record EtlProgress(int CurrentCount); - - - - internal class ConsoleColors - { - public const string Green = "\u001b[32m"; - public const string Yellow = "\u001b[33m"; - public const string Reset = "\u001b[0m"; - public const string Red = "\u001b[31m"; - public const string Cyan = "\u001b[36m"; - } } diff --git a/src/Wolfgang.Etl.Abstractions/ExtractorBase.cs b/src/Wolfgang.Etl.Abstractions/ExtractorBase.cs index 70f7dc9..68ac35b 100644 --- a/src/Wolfgang.Etl.Abstractions/ExtractorBase.cs +++ b/src/Wolfgang.Etl.Abstractions/ExtractorBase.cs @@ -28,7 +28,7 @@ public abstract class ExtractorBase /// /// The number of milliseconds between progress updates. /// - /// Value cannot be less than 1 + /// Value cannot be less than 1 public int ReportingInterval { get => _reportingInterval; @@ -50,7 +50,7 @@ public int ReportingInterval /// It is the responsibility of the derived class to keep this value up to date as the /// base class will have no way of knowing the correct value /// - + /// Value cannot be less than 0 [Range(0, int.MaxValue, ErrorMessage = "Current item count cannot be less than 0.")] public int CurrentItemCount { @@ -70,6 +70,7 @@ protected set /// /// Gets the current number of records skipped /// + /// Value cannot be less than 0 public int CurrentSkippedItemCount { get => _currentSkippedItemCount; @@ -92,7 +93,7 @@ protected set /// This is useful for partially extracting data from a source, especially when the source is large /// or infinite or during development. /// - /// The specified value is less than 0 + /// The specified value is less than 0 /// /// /// var count = 0; @@ -136,7 +137,7 @@ public int MaximumItemCount /// This is useful for partially extracting data from a source during development, or to skip /// items that were already processed or are not relevant for the current extraction. /// - /// The specified value is less than 0 + /// The specified value is less than 0 /// /// /// using (var reader = new StreamReader(filePath)) @@ -231,7 +232,7 @@ public virtual IAsyncEnumerable ExtractAsync(IProgress progr using var timer = new Timer ( _ => progress.Report(CreateProgressReport()), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(ReportingInterval) ); @@ -265,7 +266,7 @@ public virtual IAsyncEnumerable ExtractAsync(IProgress progr using var timer = new Timer ( _ => progress.Report(CreateProgressReport()), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(ReportingInterval) diff --git a/src/Wolfgang.Etl.Abstractions/LoaderBase.cs b/src/Wolfgang.Etl.Abstractions/LoaderBase.cs index b0d2f77..d613a33 100644 --- a/src/Wolfgang.Etl.Abstractions/LoaderBase.cs +++ b/src/Wolfgang.Etl.Abstractions/LoaderBase.cs @@ -29,7 +29,7 @@ public abstract class LoaderBase /// /// The number of milliseconds between progress updates. /// - /// Value cannot be less than 1 + /// Value cannot be less than 1 public int ReportingInterval { get => _reportingInterval; @@ -52,7 +52,7 @@ public int ReportingInterval /// It is the responsibility of the derived class to keep this value up to date as the /// base class will have no way of knowing the correct value /// - + /// Value cannot be less than 0 [Range(0, int.MaxValue, ErrorMessage = "Current item count cannot be less than 0.")] public int CurrentItemCount { @@ -72,6 +72,7 @@ protected set /// /// Gets the current number of records skipped /// + /// Value cannot be less than 0 public int CurrentSkippedItemCount { get => _currentSkippedItemCount; @@ -94,7 +95,7 @@ protected set /// This is useful for partially loading data from a source, especially when the source is large /// or infinite or during development. /// - /// The specified value is less than 1 + /// The specified value is less than 1 /// /// /// foreach (var item in items.Skip(SkipItemCount).Take(MaxItemCount)) @@ -109,9 +110,9 @@ public int MaximumItemCount get => _maximumItemCount; set { - if (value < 0) + if (value < 1) { - throw new ArgumentOutOfRangeException(nameof(value), "Maximum item count cannot be less than 0."); + throw new ArgumentOutOfRangeException(nameof(value), "Maximum item count cannot be less than 1."); } _maximumItemCount = value; } @@ -126,7 +127,7 @@ public int MaximumItemCount /// /// This is useful for skipping the beginning of the list during testing or because it may already be loaded /// - /// The specified value is less than 0 + /// The specified value is less than 0 /// /// /// foreach (var item in items.Skip(SkipItemCount).Take(MaxItemCount)) @@ -229,7 +230,7 @@ IProgress progress using var timer = new Timer ( _ => progress.Report(CreateProgressReport()), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(ReportingInterval) ); @@ -271,7 +272,7 @@ CancellationToken token using var timer = new Timer ( _ => progress.Report(CreateProgressReport()), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(ReportingInterval) ); @@ -336,4 +337,4 @@ protected void IncrementCurrentSkippedItemCount() Interlocked.Increment(ref _currentSkippedItemCount); } -} \ No newline at end of file +} diff --git a/src/Wolfgang.Etl.Abstractions/TransformerBase.cs b/src/Wolfgang.Etl.Abstractions/TransformerBase.cs index a1b4503..27ff694 100644 --- a/src/Wolfgang.Etl.Abstractions/TransformerBase.cs +++ b/src/Wolfgang.Etl.Abstractions/TransformerBase.cs @@ -29,7 +29,7 @@ public abstract class TransformerBase /// /// The number of milliseconds between progress updates. /// - /// Value cannot be less than 1 + /// Value cannot be less than 1 public int ReportingInterval { get => _reportingInterval; @@ -51,7 +51,7 @@ public int ReportingInterval /// It is the responsibility of the derived class to keep this value up to date as the /// base class will have no way of knowing the correct value /// - + /// Value cannot be less than 0 [Range(0, int.MaxValue, ErrorMessage = "Current item count cannot be less than 0.")] public int CurrentItemCount { @@ -71,6 +71,7 @@ protected set /// /// Gets the current number of records skipped /// + /// Value cannot be less than 0 public int CurrentSkippedItemCount { get => _currentSkippedItemCount; @@ -93,7 +94,7 @@ protected set /// This is useful for transforming a subset of data, especially when the source is large /// or infinite or during development. /// - /// The specified value is less than 1 + /// The specified value is less than 1 /// /// /// foreach (var item in items.Skip(SkipItemCount).Take(MaxItemCount)) @@ -108,9 +109,9 @@ public int MaximumItemCount get => _maximumItemCount; set { - if (value < 0) + if (value < 1) { - throw new ArgumentOutOfRangeException(nameof(value), "Maximum item count cannot be less than 0."); + throw new ArgumentOutOfRangeException(nameof(value), "Maximum item count cannot be less than 1."); } _maximumItemCount = value; } @@ -126,7 +127,7 @@ public int MaximumItemCount /// This is useful for transforming a subset of data, especially when the source is large /// or infinite or during development. /// - /// The specified value is less than 0 + /// The specified value is less than 0 /// /// /// foreach (var item in items.Skip(SkipItemCount).Take(MaxItemCount)) @@ -159,6 +160,7 @@ public int SkipItemCount /// IAsyncEnumerable<T> /// The result may be an empty sequence if no data is available or if the transformation fails. /// + /// The value of items is null public virtual IAsyncEnumerable TransformAsync ( IAsyncEnumerable items @@ -184,6 +186,7 @@ IAsyncEnumerable items /// /// /// + /// The value of items is null public virtual IAsyncEnumerable TransformAsync ( IAsyncEnumerable items, @@ -226,7 +229,7 @@ IProgress progress using var timer = new Timer ( _ => progress.Report(CreateProgressReport()), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(ReportingInterval) ); @@ -270,7 +273,7 @@ CancellationToken token using var timer = new Timer ( _ => progress.Report(CreateProgressReport()), - null, + state: null, TimeSpan.Zero, TimeSpan.FromMilliseconds(ReportingInterval) ); @@ -335,4 +338,4 @@ protected void IncrementCurrentSkippedItemCount() Interlocked.Increment(ref _currentSkippedItemCount); } -} \ No newline at end of file +}