I am delighted to announce the release of Spargine 8 (v2024.8.11.1) on November 1st, 2024. Spargine, my open-source project, now offers NuGet packages for .NET 8. These enhancements have been integrated across all my projects, many of which are currently in production. I encourage you to explore these updates and share any feedback or suggestions for further improvements.
This update introduces new methods, benchmarks, and comprehensive unit tests, all aimed at improving Spargine’s performance and reliability, resulting from over 160 commits! Notably, this release includes speed enhancements informed by benchmark tests outlined in the 4th edition of Rock Your Code: Code & App Performance for Microsoft .NET, available on Amazon.
GitHub: https://github.com/RealDotNetDave/dotNetTips.Spargine.8/releases
NuGet: http://bit.ly/dotNetDaveNuGet
You can access all the performance data for these assemblies on GitHub. I continually seek assistance with these projects. If you are interested in contributing, please don’t hesitate to contact me via email at dotnetdave@live.com. Your support and collaboration are highly appreciated!
Streamlining File Management, ULID Generation, Caching, and More
The latest release of Spargine introduces a range of powerful new features designed to enhance functionality and improve efficiency across various areas. Here are some of the highlights:
TempFileManager: Efficient Temporary File Management
I have recently migrated the TempFileManager utility from an older version of my library to the .NET platform. This useful type is designed to create and maintain a list of temporary files, simplifying file management in various scenarios, including unit testing and work-related projects.
The TempFileManager implements IDisposable, ensuring that all temporary files it manages are automatically deleted when the instance is disposed of freeing resources efficiently and reducing the risk of leaving behind orphaned files.
Key Features and Methods
Below is a list of the methods provided by the TempFileManager, each serving to streamline file creation and cleanup:
- CreateFile(): Generates a new temporary file and returns its full name and path.
- CreateFiles(int count): Creates a specified number of temporary files and returns their names and paths in a
ReadOnlyCollection. - DeleteAllFiles(): Deletes all temporary files currently managed by the instance, ensuring a complete cleanup.
- DeleteFile(string filePath): Removes a specific temporary file based on its name and path, allowing precise control over file management.
- GetManagedFiles(): Returns a
ReadOnlyCollectionof all the files currently under management, providing easy access for inspection or further processing.
This type is an invaluable tool for projects requiring temporary file handling, especially in automated tests where temporary file creation and cleanup are common tasks.
UlidGenerator: Fast and Efficient ULID Generation
I recently explored the advantages of using ULIDs (Universally Unique Lexicographically Sortable Identifiers) for generating unique IDs, particularly in applications where sorting or indexing is critical. A ULID merges the best of both worlds: it maintains the uniqueness of UUIDs (Universally Unique Identifiers) while also being lexicographically sortable by timestamp, which is especially beneficial for distributed systems that rely on chronological ordering.
Unlike traditional UUIDs, which can be randomly generated and are not inherently sortable, ULIDs offer both uniqueness and time-based ordering, making them an excellent choice for scenarios where records need to be processed in a specific order.
What is a ULID?
A typical ULID looks like this:
01F3H7RMA6P2T0W1J3C9V6QZP6
The first part represents a timestamp, while the latter part ensures uniqueness. This dual feature allows for easy sorting by creation time, solving a common challenge in distributed systems.
Introducing the UlidGenerator
To streamline the generation and use of ULIDs in .NET, I developed a type called UlidGenerator. It provides simple and efficient methods for generating these identifiers:
- GenerateUlid(): Creates a single ULID, ideal for one-off needs.
- GenerateMultipleUlids(int count): Generates a specified number of ULIDs, perfect for batch operations where multiple IDs are required.
Performance Benchmark
A significant advantage of ULIDs over traditional GUIDs (Globally Unique Identifiers) is speed. As demonstrated by benchmark tests, generating a ULID is 32 times faster than generating a GUID, making UlidGenerator an optimal solution for performance-critical applications.
JsonSerialization: Efficient Partial Deserialization of Large JSON Collections
Working with large datasets often requires more efficient handling, especially when you only need to process a portion of the total data. In high-performance applications, where resource management and speed are critical, deserializing an entire JSON collection can be overkill if you only need a subset of the data.
To address this, I implemented a new method called LoadCollectionFromJson<T>, which enables partial deserialization of a larger JSON collection. This method allows you to retrieve only a specified number of items from the collection, significantly improving performance by avoiding the overhead of deserializing unnecessary data.
Usage Example
Here is a simple example of how to use the method:
var people = JsonSerialization.LoadCollectionFromJson<Person>(json, 10);
In this example, only the first 10 items from the serialized JSON collection will be deserialized, making it a perfect solution when you do not need the entire dataset.
Optimizing with Pre-compiled Serialization
For further optimization, the method is overloaded to accept a JsonInfoType, allowing faster deserialization through pre-compiled serialization information. This technique improves performance even more by leveraging pre-built metadata for your types:
var people = JsonSerialization.LoadCollectionFromJson<Person>(json, 10, PersonJsonSerializerContext.Default.Person);
By combining selective deserialization with pre-compiled type information, this method ensures both flexibility and speed, enabling developers to manage large datasets more efficiently.
Key Benefits
- Partial Deserialization: Load only the portion of data you need.
- Performance Gains: Reduced memory usage and faster processing by avoiding unnecessary deserialization.
- Overload with JsonInfoType: Further enhances performance by using pre-compiled serialization information, reducing runtime costs.
This method is ideal for applications where large JSON datasets are common, but full deserialization is unnecessary, offering a highly efficient solution for selective data processing.
InMemoryCache: Enhanced with Per-Item Expiration and Clear Functionality
To improve flexibility and control over cached data, I have modified the InMemoryCache type to allow each item added to the cache to have its own timeout value. This new approach ensures that cached items can expire individually, based on either a TimeSpan or DateTimeOffset, offering a more granular caching strategy for time-sensitive data.
How It Works
The AddCacheItem() method now supports expiration handling, allowing you to set an expiration time for each cached item. You can specify when an item should be invalidated, either by setting a relative TimeSpan or a specific DateTimeOffset.
Here is an example demonstrating how to cache an item with a custom expiration:
var cache = InMemoryCache.Instance;
var futureDate = DateTimeOffset.Now.AddMinutes(30);
cache.AddCacheItem<Person>(string.Empty, RandomData.GeneratePersonRef(), futureDate);
In this example, the cached item will automatically expire after 30 minutes, making it easy to manage time-sensitive data without manually removing items.
Clearing the Cache
I have also introduced a Clear() method that removes all items from the cache, providing a straightforward way to reset the cache when needed:
cache.Clear();
This method is particularly useful for scenarios where you need to invalidate the entire cache, such as during application updates or data refreshes.
Key Features
- Per-Item Expiration: Each cached item can have its own timeout, providing precise control over cache lifetimes.
- Flexible Timeout Options: Expiration can be set using either TimeSpan for relative timeouts or DateTimeOffset for absolute expiration times.
- Clear Method: Easily clear all cached items when necessary, simplifying cache management.
These enhancements make the InMemoryCache type more versatile, allowing for fine-tuned caching strategies in applications where cache invalidation and control are critical.
New Methods
In this latest release of Spargine, several powerful new methods have been added, further expanding the utility and versatility of the library. Here is an overview of the key new methods:
- App.RebootComputer(): Reboots the computer programmatically, allowing for automated system restarts when necessary.
- ExceptionThrower.ThrowIOException(): Throws an IOException, useful for simulating I/O-related errors in testing scenarios.
- ExceptionThrower.ThrowJsonException(): Throws a JsonException, ideal for testing JSON-related error handling.
- ExceptionThrower.ThrowLoggableException(): Throws the Spargine-specific LoggableException, which can be used for exceptions that need to be logged for diagnostic purposes.
- LoggingHelper.LogAppDomainEvents(): Initializes logging for important application domain events such as assembly load, assembly resolve, domain unload, process exit, and type resolve. This is useful for capturing detailed operational data.
- LoggingHelper.LogAppDomainUnhandledException(): Captures and logs unhandled exceptions as critical events within the application domain, ensuring that no critical errors go unnoticed.
- NetworkHelper.GetNetworkConnections(): Returns all running network interfaces, allowing you to quickly retrieve information about the network connections on the computer.
- NetworkHelper.IsConnectedToBluetooth(): Checks if the computer is connected to a Bluetooth network, providing a simple way to monitor Bluetooth connectivity.
- RegexProcessor.RemoveHtml(): Removes HTML tags from a given input string and replaces them with a specified replacement string, useful for cleaning up text extracted from web content.
- RegexProcessor.RemoveSpecialChar(): Removes special characters from an input string and replaces them with a specified replacement string, perfect for sanitizing strings for various applications.
These new methods add more flexibility for handling system-level tasks, managing exceptions, logging application events, and performing data processing operations, making Spargine even more robust and comprehensive.
Optimized Utility Methods: Enhancements Across Arrays, Collections, Dictionaries, Lists, and More
This release of Spargine brings several optimized utility methods designed to enhance performance and usability across a variety of data types in .NET. From arrays and collections to numeric operations and object handling, these new methods streamline common operations, making your code faster and more efficient.
ArrayExtensions
The ArrayExtensions type adds new extension methods for arrays, providing additional functionality:
- AsReadOnlySpan(): Converts an array to a ReadOnlySpan, allowing you to safely interact with the array’s data without modifying it.
- AsSpan(): Converts an array to a Span, offering a flexible way to work with contiguous memory regions.
- ToFrozenSet(): Converts an array to a FrozenSet, providing a highly optimized set that improves lookup performance, especially for large datasets.
CollectionExtensions
The CollectionExtensions type adds methods for enhancing collections, providing more flexible ways to manage data:
- AsReadOnlySpan(): Converts a collection to a ReadOnlySpan.
- AsSpan(): Converts a collection to a Span.
- ToFrozenSet(): Converts a collection to a FrozenSet, offering efficient, immutable collections.
DictionaryExtensions
Enhancements to dictionaries are available through DictionaryExtensions, improving data manipulation:
- ToFrozenDictionary(): Converts an IDictionary collection to a FrozenDictionary, which improves lookup performance while maintaining immutability.
- ToImmutableSortedDictionary(): Converts an IDictionary collection to an ImmutableSortedDictionary, ensuring both immutability and sorted order.
- ToReadOnlyDictionary(): Converts an IDictionary collection to a ReadOnlyDictionary.
EnumerableExtensions
EnumerableExtensions enhances the functionality of IEnumerable types with performance-optimized methods:
- FastModifyCollection(): Modifies each item in the collection using the specified function and returns a read-only collection of the modified items, ensuring efficient data transformation.
- ToFrozenSet(): Converts an
IEnumerablecollection to aFrozenSetfor faster lookups.
ListExtensions
For list-based collections, ListExtensions introduces:
- ToFrozenSet(): Converts a List to a
FrozenSet, leveraging immutable, high-performance data structures.
NumericExtensions
NumericExtensions simplifies working with numeric types by introducing:
- BytesToMegabytes(): Converts the number of bytes into megabytes, making it easy to manage byte-size conversions.
ObjectExtensions
ObjectExtensions adds utility methods to enhance object handling:
- FastGetHashCode(): Generates a hash code for an object using
RuntimeHelpers.GetHashCode(), ensuring faster hash code generation for use in collections or other hash-based operations.
These optimizations across several types in Spargine enable more efficient data handling and processing, providing better performance in your applications.
Performance Enhancements: Significant Speed Improvements Across Methods
With each new release of Spargine, significant performance optimizations have been implemented, resulting in substantial speed improvements across various methods. Below are some of the most notable enhancements:
- FastStringBuilder.ConcatStrings(): Now 7.1 times faster, improving performance for string concatenation operations.
- LoggingHelper.RetrieveAllExceptions(): Enhanced by 1.7 times, making exception retrieval quicker and more efficient.
- ArrayExtensions.RemoveFirst(): Delivers a 5.2 times performance increase when removing the first element from an array.
- ArrayExtensions.RemoveLast(): Boasts an impressive 18 times speed boost for removing the last element from an array.
- AssemblyExtensions.GetAllTypes(): Now 1.2 times faster, enhancing type retrieval across assemblies.
- EnumerableExtensions.AddDistinct(): Improves performance by 4.7 times when adding distinct elements to collections.
- EnumerableExtensions.IndexOf(): Delivers a 2.5 times speed boost when searching for an element’s index.
- EnumerableExtensions.IndexOf() with Comparer: Enhanced by 1.35 times for index searches using a custom comparer.
- EnumerableExtensions.ToDelimitedString(): Now 1.06 times faster when converting collections to delimited strings.
- ListExtensions.AsReadOnlySpan(): Achieves an astonishing 840 times improvement, significantly speeding up the process of converting lists to ReadOnlySpan.
- ListExtensions.GenerateHashCode(): A remarkable 19,779 times faster in generating hash codes for lists, making it ideal for high-performance scenarios.
- ListExtensions.HasItems() with Predicate: Executes 50 times faster, streamlining checks for items in lists using predicates.
- ListExtensions.IsEqualTo(): Now 54 times faster, enhancing list equality comparisons.
- ListExtensions.ToImmutableArray(): Offers a 2 times speed boost when converting lists to immutable arrays.
- ObservableCollectionExtensions.DoesNotHaveItems(): Now 2 times faster in checking for empty collections.
Additionally, the RandomData type within the DotNetTips.Spargine.8.Tester assembly has seen slight performance improvements across most methods. These optimizations were achieved by applying the [MethodImpl(MethodImplOptions.AggressiveInlining)] attribute, reducing overhead in method execution.
These performance improvements ensure that Spargine remains a top choice for high-performance .NET development, delivering faster and more efficient functionality across a wide range of operations.
Summary
I trust these new and enhanced methods in Spargine will significantly benefit your projects by boosting performance and reliability. Detailed benchmark results are available on GitHub. As always, the success of open-source projects like Spargine relies heavily on community involvement. If you find these updates useful or have ideas for further improvements, I encourage you to contribute. Whether it is by submitting a pull request, reporting issues, or suggesting new features, your input is invaluable.
Together, we can continue to make Spargine a robust and indispensable tool for the .NET community. Your feedback and suggestions are highly appreciated, so please share them in the comments section.
Thank you for your support and keep coding faster and better!
Pick up any books by David McCarter by going to Amazon.com: http://bit.ly/RockYourCodeBooks
Make a one-time donation
Make a monthly donation
Make a yearly donation
Choose an amount
Or enter a custom amount
Your contribution is appreciated.
Your contribution is appreciated.
Your contribution is appreciated.
DonateDonate monthlyDonate yearlyIf you liked this article, please buy David a cup of Coffee by going here: https://www.buymeacoffee.com/dotnetdave
© The information in this article is copywritten and cannot be preproduced in any way without express permission from David McCarter.
Discover more from dotNetTips.com
Subscribe to get the latest posts sent to your email.

