Coding Faster With The dotNetTips Spargine NuGet MAR 2021 Update

Announcing dotNetTips Spargine

 
I’m proud to announce the second version (v2021.5.1.50) of Spargine, my brand new open-source projects, and NuGet packages for .NET 5 and above. I’ve added many new classes, methods, and unit tests! I have been working hard the last few months adding more classes and methods, along with more unit testing and benchmarking. I use these in all the projects I’m currently working on including some that are in production! I hope you will check them out and let me know what you would like to see added.
 
Coding Faster With The dotNetTips Spargine NuGet MAR 2021 Update

Removing Carriage Return/ Line Feed from Strings

 
Recently at work, we ran into an issue where if we export data as CSV that any of the string columns that have a carriage return and/ or line feeds in them, breaks our import process. It seems the NuGet package we are using does not support that. Also, we researched and found many others seem to have the same issue. Since we needed to get the release out, we decided to strip out the carriage return/ line feeds from text fields. Not the best solution, but it will buy us time until we can research this issue more.
I added RemoveCRLF() as an extension method for strings. Here is how to use it.
  1. var testValue = RandomData.GenerateWord(10) + ControlChars.NewLine +   
  2.                 RandomData.GenerateWord(15) + ControlChars.CRLF +   
  3.                 RandomData.GenerateWord(15);  
  4.   
  5. var result = testValue.RemoveCRLF(replacement: ".");  
This method uses a regular expression as shown below,
  1. Regex.Replace(input, @"[\r\n]+", replacement, RegexOptions.IgnoreCase | RegexOptions.Compiled);  
According to the benchmark tests I did, this method speed is around 460 ns.
 

Retrieving Property Values

 
To retrieve all the property values from an object as an ImmutableDictionary, you can use GetPropertyValues() method in the TypeHelper class as shown below.
  1. var person = RandomData.GeneratePerson<PersonProper>();  
  2. var result = TypeHelper.GetPropertyValues(person);  
Here is an example of the what the output looks like in the debugger.
 
Coding Faster With The dotNetTips Spargine NuGet MAR 2021 Update
 

Is Type a Collection?

 
While working on some code recently, I was looking at Type and noticed that is has a method called IsArray() but what if I want to tell if a collection that implements IEnumerable? I added an extension method called IsEnumerable(). Here is how to use it.
  1. var people = RandomData.GeneratePersonCollection<PersonProper>(10).AsEnumerable();    
  2. var result = people.GetType().IsEnumerable();    

Safely Loading a List of Files

 
One of the drawbacks to the DirectoryInfo.EnumerateFiles() method is that if it hits a DirectoryNotFoundException or SecurityException, the call will fail if even one file access might throw these exceptions. Since .NET does not help with this, I created an extension method called SafeFileSearch(). SafeFileSearch() takes in an IEnumerable of DirectoryInfo and will continue to the next directory even if one of these two exceptions are thrown. Many folders in Windows will throw these exceptions.
 
Here is how to use it,
  1. searchFolders.Add(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles)));  
  2. searchFolders.Add(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFilesX86)));  
  3. searchFolders.Add(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)));  
  4.   
  5. foreach (var file in DirectoryHelper.SafeFileSearch(searchFolders, searchPattern: "*.*", searchOption: SearchOption.AllDirectories))  
  6. {  
  7.     // DO SOMETHING  
  8. }  

Data Validation

 
I am always adding more and more data validation methods to Validate.cs. Here is what I added for this release.
 

Validating for Null

 
If you want to validate data for null and then throw an exception, if there is one, you can use the TryValidateNull(). Here is an example,
  1. try  
  2. {  
  3.     personProper = RandomData.GeneratePerson<PersonProper>();  
  4.     Validate.TryValidateNull(personProper, throwException: true);  
  5. }  
  6. catch  
  7. {  
  8.      /// Something bad happened!  
  9. }  
If the value is null, this method will throw the InvalidValueException<> exception.
 

Validating Parameters for Null

 
If you need to check parameters for null, then I have added a method called TryValidateNullParam that will throw an ArgumentNullException if a null is found. Here is an example from one of the methods in Spargine,
  1. public BadRequestObjectResult LogErrorCreateBadRequest(string errorMessage, Exception ex, ILogger logger)  
  2. {  
  3.     Validate.TryValidateParam(errorMessage, nameof(errorMessage));  
  4.     Validate.TryValidateNullParam(ex, nameof(ex));  
  5.   
  6.     if (logger.IsNotNull())  
  7.     {  
  8.           logger.LogError(ex, message: $"{errorMessage}");  
  9.     }  
  10.   
  11.     return _controller.BadRequest(new ProblemDetails { Title = errorMessage, Detail = ex.GetAllMessages() });  
  12. }  

Validating an Object

 
To use your own Boolean statement to validate an object, I have added TryValidateObject(). Since developers are not supposed to throw the Exception type itself, this method prevents this (for your more junior developers on your team)! Here is an example,
  1. Validate.TryValidateObject<NullReferenceException>(condition: personProper.Id.IsNotEmpty(), "Person is missing Id.");  

Base Interface for the New Record Type

 
In an article that I wrote in February, I talked about how much I like the new record class type in .NET 5. I stated that most of or all model classes should use record to make them immutable and helps us not to write so much boilerplate code. Since most of these types need a few extra methods, I created a new interface type called IDataRecord. It currently has a property called Id since most of these types need one. Since ToString() for record types does not properly turn all the property values into a string, I added AllPropertiesToString() that will (it uses reflection)! We use this where I work to log changes to our data models. Here is how I use IDataRecord in my testing assembly.
  1. public record AddressRecord : IDataRecord  
  2. {  
  3.     public AddressRecord()  
  4.     { }  
  5.   
  6.     public AddressRecord(string id)  
  7.     {  
  8.         this.Id = id;  
  9.     }  
  10.   
  11.     public string Id { get; init; }  
  12.   
  13.     /// Code removed for brevity  
  14. }  
I am also using IDataRecord to create helper methods for types that implement it. Stay tuned for more information.
 

Creating A BlockingCollection

 
With many apps using more and more multi-threading, it is important to not forget about collections. Using types like List<> in many cases will not work properly. One of the types you can use is the BlockingCollection<>. Since I need something like this in my re-write of my app for developers, I decided to make it easier to create one from any collection that supports IEnumerable<>. The extension method is called ToBlockingCollection<>. I hope you will try it out. Here are the benchmark results for this release.
 
Coding Faster With The dotNetTips Spargine NuGet MAR 2021 Update 
 

Moving a File with More Options

 
Currently the File.Move method does not use all the options that the Windows API call does. So, I created a new method in the FileHelper type called MoveFile() that does! Here is a list of the options:
  1. CopyAllowed
    If the file is to be moved to a different volume. If the file is successfully copied to a different volume and the original file is unable to be deleted, the function succeeds leaving the source file intact. This value cannot be used with DelayUntilReboot.

  2. DelayUntilReboot
    The system does not move the file until the operating system is restarted. The system moves the file immediately after AUTOCHK is executed, but before creating any paging files. Consequently, this parameter enables the function to delete paging files from previous startups. This value can be used only if the process is in the context of a user who belongs to the administrator’s group or the LocalSystem account. This value cannot be used with CopyAllowed.

  3. FailIfNotTrackable
    The function fails if the source file is a link source, but the file cannot be tracked after the move. This situation can occur if the destination is a volume formatted with the FAT file system.

  4. ReplaceExsiting
    If the destination file exists, the function replaces its contents with the contents of the existing file, if security requirements regarding access control lists (ACLs) are met.

  5. WriteThrough
    The function does not return until the file is moved on the disk. Setting this value guarantees that a move performed as a copy and delete operation is flushed to disk before the function returns. The flush occurs at the end of the copy operation.
  1. FileHelper.MoveFile(file, newFile, FileMoveOptions.ReplaceExisting |  FileMoveOptions.WriteThrough);  
There is no magic going on here, I am just using the Windows MoveFileEx API call. If you need more options, I hope this method does the trick for you!
 

Other New Methods

 
Here are just some of the other new methods that I have added to this release.
  1. DateTime Extensions:
    1. FromMilliEpochTime()
    2. FromUnixTime()
    3. ToMilliEpochTime()
    4. ToUnixTime()
  2. DirectoryHelper:
    1. AddDataFolder(): Retrieves the proper application data folder for either Windows or macOS.
    2. CopyDirectory(): Includes flag to overwrite the destination directory.
    3. DeleteDirectory(): Includes override that includes the number of retries before it fails.
    4. DeleteDirectoryAsync()
    5. SetFileAttributesToNormal(): Sets all the files in a path FileAttribute to Normal.
  3. StringBuilderHelper:
    1. BytesToString()
  4. AssemblyExtensions:
    1. GetAllTypes(): Retrieves a list of all the types in an assembly.
    2. GetAllInstances(): Retrieves a list of all instances of a type in an assembly.
    3. GetAllInterfaces(): Retrieves a list of all the interfaces in an assembly.
    4. GetTypes(): Retrieves a list of a specific type in an assembly.
  5. StringExtensions:
    1. IsGuid()
    2. IsMacAddress()

Summary

 
On another note, I read that using the ObjectPool might speed things up, but bases on my benchmark tests using that along with StringBuilder I could not see a performance gain, actually the opposite, so I took it out of the library.
 
I hope you will check out these methods or any of the others in the assemblies. Stay tuned to this site for upcoming news about my new .NET 5 open-source assemblies and NuGet packages!
 
If you have any comments or suggestions, please comment below. I am always looking for new things for these open-source projects! Feel free to make changes to this repository and submit a pull request!


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