Coding Faster with dotNetTips Spargine: Validating Arguments Made Easy with Validator

Spargine is a collection of open-source assemblies and NuGet packages designed for .NET 10, which I have been developing and maintaining since the release of .NET Framework 2. These assemblies are not only a core part of my projects but are also actively deployed in production environments across several companies I collaborate with.

Get Spargine

You can access the source code and NuGet packages here:

Throughout my career as a software engineer, I have analyzed millions of lines of code and noticed a recurring issue: insufficient validation of method and property inputs. While it may seem like a minor concern, proper validation is crucialbad input leads to bad output. If erroneous data makes its way into a database, fixing it can be a nightmare. Moreover, neglecting validation violates encapsulation and object-oriented programming principles.

To address this, Spargine has always included helper methods for validation. In the later versions, I have rewritten these methods to make data validation even more accessible using fluent validation techniques. I hope that the .NET team at Microsoft considers incorporating similar helper methods into .NET in the future. Until then, teams can take advantage of the Validator class available in DotNetTips.Spargine.Core.

Effortless Method and Property Validation

This article provides an overview of the available validation methods, along with practical examples. These methods return the validated data, allowing seamless integration into your application logic. Most of them also support specifying a default value in case validation fails.

For exception handling, these methods follow best practices, throwing ArgumentException or derived exceptions when necessary. If a custom error message is not provided, a default message is used.

A key feature is that all methods include a paramName parameter, but you do not need to pass it explicitly—they automatically infer the name using the CallerArgumentExpression attribute.

Validation Methods

Below are the core validation methods available in Spargine’s Validator class:

  • ArgumentCountInRange<T>(int min, int max)
    Validates that the collection has a count within the specified minimum and maximum range.
  • ArgumentCustom<T>(Func<T, bool> predicate)
    Validates the input using a custom predicate function.
  • ArgumentDefined<T>(string errorMessage) where T : Enum
    Tries to validate the Enum input.
  • ArgumentEquals(this Type input, Type expectedType):  Tries to validate that the Type matches the expected Type.
  • ArgumentExists(DirectoryInfo? defaultValue = null)
    Checks that the DirectoryInfo exists.
  • ArgumentExists(FileInfo? defaultValue = null)
    Validates that the FileInfo exists.
  • ArgumentInRange(in byte lower, in byte upper)
    Validates that the byte is within the specified range.
    This method also works with DateTime, DateTimeOffset, integer, double, long, decimal, DateOnly, TimeOnly, and string.
  • ArgumentItemsExists<T>()
    Tries to validate the IEnumerable<T> is not null and has items.
    This method is overloaded to work with IReadOnlyList<T>, IReadOnlyCollection<T>, List<T>, array, and IEnumerable<T>.
  • ArgumentMatched(Regex match, bool trim, string? defaultValue)
    Tries to validate the string using Regex.
  • ArgumentMeetsCondition<T>(bool condition, T? defaultValue)
    Checks a condition against the input with a default value.
  • ArgumentNotEmpty<T>()
    Determines whether the ReadOnlySpan<T> is empty.
    This method is overloaded to work with Span<T>.
  • ArgumentNotNull<T>()
    Validates that the Collection<T> is not null.
    This method is overloaded to work with T, and Uri.
  • ArgumentNotNullOrEmpty(Guid defaultValue)
    Tries to validate that the Guid is not empty.
    This method is overloaded to work with a string.
  • ArgumentNotReadOnly<T>()
    Validates that the IList<T> is not read-only.
    This method is overloaded to work with ICollection<T>.
  • ArgumentTypeEquals(Type expectedType)
    Tries to validate that the Type matches the expected Type.

Methods in Action

Checking for Null

Always ensure reference type parameters are not null. A null value is a common source of errors that frequently appears in logs and bug reports.

Use ArgumentNotNull() to validate inputs:

public static async Task<string> DownloadStringAsync(Uri address)
{
    address = address.ArgumentNotNull();

    // Code removed for brevity
}

Ensuring Non-Null and Non-Empty Values

To validate that a string or collection is neither null nor empty, use ArgumentNotNullOrEmpty().

Example:

public bool IsLocalUri(string path, HttpRequest request)
{
    path = path.ArgumentNotNullOrEmpty(trim: true);
    request = request.ArgumentNotNull();

    // Code removed for brevity
}

Validating Data with a Condition

Use ArgumentMeetsCondition() to ensure an input meets a specific Boolean condition:

public static byte[] GenerateByteArray(double sizeInKb)
{
    sizeInKb = sizeInKb.ArgumentMeetsCondition(sizeInKb >= double.Epsilon,
               errorMessage: string.Format(CultureInfo.InvariantCulture,
               Resources.SizeMustBeEpsilon, double.Epsilon));

    // Code removed for brevity
}

Validating Data with Regular Expressions

To enforce pattern-based validation, use ArgumentMatched():

var expression = new Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");

userEmail = userEmail.ArgumentMatched(expression);

Validating Collections

Before accessing a collection, ensure it contains items using ArgumentItemsExist():

var person = personCollection[2]; // Unsafe: Could throw an exception if empty!

// Corrected version:
public int CopyFiles(IEnumerable<FileInfo> files, DirectoryInfo destination)
{
    var list = files.ArgumentItemsExists().ToArray();

    // Code removed for brevity
}

You can also validate the collection size:

var people = people.ArgumentItemsExists(count: 10);

Ensuring Values Are in a Range

For integers, dates, and other data types, ensure values fall within a specified range using ArgumentInRange():

public static bool HasValue(this string input, int length)
{
    length = length.ArgumentInRange(lower: 1, upper: length);

    return input is not null && (input.Trim().Length == length);
}

Ensuring the Existence of Directories and Files

Before accessing directories or files, confirm their existence with ArgumentExists():

public static ReadOnlyCollection<Type> FindDerivedTypes(DirectoryInfo path, SearchOption fileSearchType, Type baseType, bool classOnly)
{

    var files = path.ArgumentExists().EnumerateFiles("*.dll", fileSearchType).ToImmutableArray();
}

Validating Enum Values

Invalid Enum values can cause unexpected exceptions. Use ArgumentDefined() to prevent this:

public static string GetDescription(this Enum input)
{
    input = input.ArgumentDefined();

    // Code removed for brevity
}

Validating Type Matches

Ensure an object’s type matches an expected type using ArgumentEquals():

var result = person.GetType().ArgumentEquals(typeof(Person));

Conclusion

I use these Validator methods extensively to enforce data integrity and correctness in my applications. By incorporating these validation techniques, developers can write cleaner, safer, and more maintainable code.

For hundreds of additional examples, check out the Spargine source code.

Get Involved!

The success of open-source projects like Spargine relies on community contributions. If you find these updates useful or have ideas for further improvements, I encourage you to contribute by:

  • Submitting pull requests
  • Reporting issues
  • Suggesting new features

Your input is invaluable in making Spargine an even more powerful tool for the .NET community.

If you are interested in contributing or have any questions, feel free to contact me via email at dotnetdave@live.com. Your support and collaboration are greatly appreciated!

Thank you, and happy coding!

Pick up any books by David McCarter by going to Amazon.com: http://bit.ly/RockYourCodeBooks

One-Time
Monthly
Yearly

Make a one-time donation

Make a monthly donation

Make a yearly donation

Choose an amount

$5.00
$15.00
$100.00
$5.00
$15.00
$100.00
$5.00
$15.00
$100.00

Or enter a custom amount

$

Your contribution is appreciated.

Your contribution is appreciated.

Your contribution is appreciated.

DonateDonate monthlyDonate yearly

If 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.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.