Coding Faster With dotNetTips Spargine - September 2021 Release

I am happy to announce the fourth release (v2021.9.4.23) of Spargine, my brand new open-source projects, and NuGet packages for .NET 5 and above. I have added new classes, methods, and unit tests! I use these in all the projects I am currently working on including many that are in production! I hope you will check them out and let me know what you would like to see added.

GitHub: https://github.com/RealDotNetDave/dotNetTips.Utility.Core/releases
NuGet: http://bit.ly/dotNetDaveNuGet

This release includes performance changes too. All the performance data for these assemblies can be found on GitHub. I am always looking for help with these projects, especially writing more unit tests. If you would like to help, please email me at [email protected].

Simple String Encryption/ Decryption

I wanted to make it simple to encrypt and decrypt a string using a simple key (password). This can be used to secure strings used in applications. For example, you could use this to encrypt a connection to Azure queues that you include in your app. The two methods shown below in the EncryptionHelper class uses Aes security.

Example

var cipherText = EncryptionHelper.SimpleEncrypt("TestString", "TestKey");
var plainText = EncryptionHelper.SimpleDecrypt(cipherText, "TestKey");

Example Output

Here is an example output from SimpleEncript():

pVGs2TkJkzcHYW3Wiq2QEx8/kyFBJmE2Ji2lbwwAPaA=

As part of getting this into Spargine I copied the EncryptionHelper code for the following methods from my .NET Core Open-Source project.

AesEncrypt(string plainText, byte[] key, byte[] iv)
AesDecrypt(string cipherText, byte[] key, byte[] iv)

Implementing a Singleton

The Singleton design pattern is used often in programming to ensure there is only one instance of an object. I typically use this for the configuration object in my apps. I have previously written about this pattern but always wanted to come up with a better way. After watching the C# Design Patterns: Singleton course by Steve Smith on Pluralsight, I implemented a generic class called Singleton<T> based on his design that is thread-safe and lazy loads the instance.

Below is an example of how to use Singleton<T>.

var list = Singleton<ObservableList<string>>.Instance;

You can also make any type of singleton by using Dependency Injection. Steve shows you how to do that in his course. Steve has also been a guest on my show Rockin’ the Code World with dotNetDave on C# Corner Live. I hope you will watch it.

Running Async Tasks Synchronously

In .NET, there are more and more methods that support async, but there are many times we just want to run them synchronously. Thankfully, I ran across an article by Rick Strahl that shows how to do this. I added the RunSync() methods to a new class in Core called TaskHelper.

RunSync() has 4 overloads and here is how to use them. First, I start off with a method called Fire() and FireWithReturn() to test these methods and it looks like this.

private async Task Fire(string input) 
{
    this._fireResult = input;
    Console.WriteLine(input);
    await Task.Delay(1);
}

private async Task < string > FireWithReturn(string input) 
{
    this._fireResult = input;
    Console.WriteLine(input);
    await Task.Delay(1);
    return input;
}

Here are the four different ways you can use these methods.

#1
TaskHelper.RunSync(() => this.Fire("Test Message"));

#2
var cancelToken = new CancellationTokenSource().Token;
TaskHelper.RunSync(() => this.Fire("Test Message", cancellationToken: cancelToken);

#3
TaskHelper.RunSync(() => this.FireWithReturn("Test Message"));

#4
var cancelToken = new CancellationTokenSource().Token;
TaskHelper.RunSync(() => this.FireWithReturn("Test Message"), cancellationToken: cancelToken);

With some of these methods, you can also use TaskCreationOptions, TaskContinutationOptions, and TaskScheduler.

Recommendation to API Developers

My recommendation to API developers is that unless .NET goes 100% async (like some languages are), then please also provide a synchronous version of the methods too.

Fire and Forget Async Methods

In the same article by Rick Strahl, he showed a great way to fire and forget async methods. I’ve wanted something like this for a long time. The code that I added to TaskExtensions in Core is very simple to use and I test it with the same Fire() method.

#1
this.Fire("Test Message").FireAndForget();

#2
Action<Exception> exAction = (Exception ex) => Debug.WriteLine(ex.Message);
this.Fire("Test Message").FireAndForget(exAction);

Safely Searching a Directory

One issue with the methods in .NET to search for files is that if you choose SearchOption.AllDirectories and the user does not have access to even one of the sub-directories, the entire call will fail. For me, I just want it to ignore the errors and keep processing. I’ve added a new method called SafeDirectoryContainsAny() method in the DirectoryHelper class that returns a list of directories that contains any files from the search patterns.

Here is an example of how to use it.

var searchFolders = new List < DirectoryInfo > ();
searchFolders.Add(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles)));
searchFolders.Add(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic)));
searchFolders.Add(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)));
searchFolders.Add(new DirectoryInfo(Environment.GetEnvironmentVariable(EnvironmentKey.TEMP.ToString())));
var directories = new List < DirectoryInfo > ();

foreach(var directory in searchFolders) 
{
    if (DirectoryHelper.SafeDirectoryContainsAny(directory, SearchOption.AllDirectories, "*.txt", "*.tmp", "*.doc", "*.docx", "*.dll")) 
    {
        directories.Add(directory);
    }
}

How to Create a Thread-Safe Queue

Back in version 4 of the .NET Framework, the team added a thread-safe queue (FIFO) called ConcurrentQueue. In .NET Core 3, they added another new class that can also be used as a thread-safe FIFO queue called Channel. Thinking of performance, I wondered which one would be faster. So, in the Core assembly, I created a new type called ChannelQueue. Its methods and properties are:

  • Count(): Retrieves the current number of items in the queue.
  • ListenAsync(): Listens for new items added to the queue.
  • Lock(): Locks the queue so no more items can be added. This must be called to complete ListenAsync().
  • ReadAsync(): Reads a single item from the queue.
  • WriteAsync(): Add an item or an entire collection to the queue.

Example

Here is how to use the method read and write methods.

var queue = new ChannelQueue < PersonProper > ();
var people = RandomData.GeneratePersonCollection < PersonProper > (100);
var token = CancellationToken.None;

/// Write
foreach(var person in people) 
{
    await queue.WriteAsync(person, token);
}

/// Read
do 
{
    var item = await queue.ReadAsync(token);
} while (queue.Count > 0);

Benchmark Results

I found the benchmark results for these two types of queues interesting. These are the results of adding an item to the queue.

Coding Faster with dotNetTips Spargine

As you can see, adding an item to the ConcurrentQueue is much, much fast than adding an item to the ChannelQueue. This test did not include two threads adding items to the queue at the same time. I wanted to test two threads retrieving items from the queues. Those results are below.

Coding Faster with dotNetTips Spargine

The benchmark results show that reading items from the queues by using two threads is close to the same performance.

Even More New Code

Here are some more new methods added to this release:

  • PickRandom<T>() for ReadOnlySpan<T>.
  • BytesToString() for ReadOnlySpan<byte>.
  • IsAsciiDigit() in StringExtensions.
  • IsAsciiWhitespace() in StringExtensions.
  • TryValidateParam() for ReadOnlySpan<T> in Validate.
  • TryValidateParam() for Span<T> in Validate.

Breaking Changes

I noticed that the custom collection types in Spargine, were in the dotNetTips.Spargine.5 project, but they really should be in the dotNetTips.Spargine.Core assembly for greater reuse, so I moved them. Those classes are,

  • ConcurrentHashSet
  • DistinctConcurrentBag
  • FastSortedList
  • ObservableList
  • DistinctBlockingCollection

Summary

I hope you will check out these methods or any of the others in the assemblies. I am also looking for contributors to these .NET 5 projects. If you have any comments or suggestions, please comment below.


Similar Articles
McCarter Consulting
Software architecture, code & app performance, code quality, Microsoft .NET & mentoring. Available!