DotNetToolbox.Core
8.0.3
See the version list below for details.
dotnet add package DotNetToolbox.Core --version 8.0.3
NuGet\Install-Package DotNetToolbox.Core -Version 8.0.3
<PackageReference Include="DotNetToolbox.Core" Version="8.0.3" />
paket add DotNetToolbox.Core --version 8.0.3
#r "nuget: DotNetToolbox.Core, 8.0.3"
// Install DotNetToolbox.Core as a Cake Addin #addin nuget:?package=DotNetToolbox.Core&version=8.0.3 // Install DotNetToolbox.Core as a Cake Tool #tool nuget:?package=DotNetToolbox.Core&version=8.0.3
Core (DotNetToolbox.Core)
Introduction
DotNetToolbox.Core is a versatile C# library for .NET 8, designed to enhance development by providing a range of utilities and patterns. It simplifies complex tasks and improves code testability.
Table of Contents
- Installation
- Dependencies
- Key Features
- Result Pattern
- System Utilities
- Pagination Utilities
- Singleton and Options Patterns
- Extension Classes
- Other Utilities
Installation
PM> Install-Package DotNetToolbox.Core
Dependencies
- .NET 8
Result Pattern
- ValidationError: Core class for representing generic validation errors.
The ValidationError
class represents individual validation errors, useful for detailed error tracking in data validation scenarios.
Examples:
Creating a ValidationError:
// Create a validation error with a source and message ValidationError emailError = new ValidationError("Email", "Invalid email format"); Console.WriteLine(emailError); // Create a validation error with only a message ValidationError generalError = new ValidationError("General error occurred"); Console.WriteLine(generalError);
Using Implicit Conversion:
ValidationError implicitError = "Username is required"; Console.WriteLine(implicitError);
It has extension methods for collections of ValidationError
s, such as checking if a specific error is contained within the collection.
Check for an error in a collection:
List<ValidationError> errors = new List<ValidationError> { "Email is required", new ValidationError("Password", "Password is too weak") }; // Check if a specific error message exists in the collection bool hasEmailError = errors.Contains("Email", "Email is required"); Console.WriteLine($"Email error present: {hasEmailError}");
It also works with a custom exception class that encapsulates a collection of
ValidationError
s, intended to be thrown when validation fails.Check for an error in a collection:
try { var result = ValidateUserData(userData); if (result.IsInvalid) { throw new ValidationException(result.Errors); } } catch (ValidationException ex) { foreach (var error in ex.Errors) { Console.WriteLine(error); } }
- Result: Core class for generic operation results. Used mostly for validation and error handling.
The Result
class provides a flexible way to represent the outcome of operations, whether successful, invalid due to data issues, or errored due to exceptions.
Examples:
Creating a Successful Result:
var successResult = Result.Success(); Console.WriteLine($"Operation Successful: {successResult.IsSuccess}");
Creating a Result with Data Validation Error:
var invalidResult = Result.InvalidData("Invalid input data"); Console.WriteLine($"Data is Valid: {!invalidResult.IsInvalid}");
Creating a Result Representing an Error:
var errorResult = Result.Error(new Exception("Unexpected error occurred")); Console.WriteLine($"Operation Successful: {errorResult.IsSuccess}");
Using Asynchronous Methods:
async Task PerformOperationAsync() { var result = await Result.ErrorTask("Async operation failed"); Console.WriteLine($"Operation Successful: {result.IsSuccess}"); }
Implicit Conversion from
ValidationError
orException
:Result resultFromError = new ValidationError("Email", "Invalid email format"); Result resultFromException = new Exception("Database connection failed");
The Result<TValue>
class extends the functionality of the Result
class, allowing you to include a value with the operation outcome. It is particularly useful when an operation needs to return a value on success. Below are examples of how to use the Result<TValue>
class:
Examples:
Returning a Value on Successful Operation:
Result<int> CalculateSquare(int number) { int square = number * number; return Result.Success(square); } var squareResult = CalculateSquare(5); if (squareResult.IsSuccess) { Console.WriteLine($"Square of 5 is {squareResult.Value}"); }
Handling an Error with
Result<TValue>
:Result<string> FetchData(string url) { try { // Assuming GetData is a method that fetches data from a source string data = GetData(url); return Result.Success(data); } catch (Exception ex) { return Result<string>.Error(ex); } } var apiResult = FetchDataFromApi("https://example.com/data"); if (apiResult.IsSuccess) { Console.WriteLine($"Fetched data: {apiResult.Value}"); } else { Console.WriteLine("Failed to fetch data"); }
- CrudResult.cs: Specialized for CRUD operations.
The CrudResult
class is designed for outcomes of CRUD operations, providing specific status types like NotFound
, Conflict
, etc. Here are some examples:
Examples:
Indicating a Successful CRUD Operation:
CrudResult CreateRecord(Record record) { // Logic to create a record return CrudResult.Success(); } var creationResult = CreateRecord(new Record()); if (creationResult.IsSuccess) { Console.WriteLine("Record created successfully."); }
Handling a 'Not Found' Scenario in CRUD Operations:
CrudResult DeleteRecord(int id) { // Logic to delete a record if (recordNotFound) { return CrudResult.NotFound(); } return CrudResult.Success(); } var deletionResult = DeleteRecord(5); if (deletionResult.WasNotFound) { Console.WriteLine("Record not found."); }
CrudResult<TValue>
extends CrudResult
to include a value with the operation outcome, useful for operations like 'Read'.
Examples:
Returning a Value on Successful 'Read' Operation:
CrudResult<Record> ReadRecord(int id) { Record? record = // Logic to read a record if (record == null) { return CrudResult<Record>.NotFound(); } return CrudResult<Record>.Success(record); } var readResult = ReadRecord(3); if (readResult.IsSuccess) { Console.WriteLine($"Record read: {readResult.Value}"); } else if (readResult.WasNotFound) { Console.WriteLine("Record not found."); }
Handling a 'Conflict' in CRUD Operations:
CrudResult<Record> UpdateRecord(Record updatedRecord) { // Logic to update a record if (recordHasConflict) { return CrudResult<Record>.Conflict(); } return CrudResult<Record>.Success(updatedRecord); } var updateResult = UpdateRecord(new Record()); if (updateResult.HasConflict) { Console.WriteLine("Record update conflict occurred."); }
- HttpResult.cs: Tailored for HTTP transactions.
The HttpResult
class is designed to represent the outcome of HTTP operations, mapping closely to HTTP response statuses. Here are some examples:
Examples:
Returning an 'Ok' HTTP Response:
HttpResult GetUserData() { // Logic to get user data return HttpResult.Ok(); } var response = GetUserData(); if (response.IsOk) { Console.WriteLine("User data retrieved successfully."); }
Handling a 'Bad Request' Scenario:
HttpResult UpdateUserProfile(UserProfile profile) { if (!IsValid(profile)) { return HttpResult.BadRequest(Result.InvalidData("Invalid profile data")); } // Update logic return HttpResult.Ok(); } var updateResponse = UpdateUserProfile(new UserProfile()); if (updateResponse.IsBadRequest) { Console.WriteLine("Profile update failed due to invalid data."); }
HttpResult<TValue>
extends HttpResult
to include a value with the HTTP response, useful for GET requests or any operation returning data.
Examples:
Returning Data on Successful Operation:
HttpResult<UserProfile> GetUserProfile(int userId) { UserProfile? userProfile = // Fetch user profile logic if (userProfile == null) { return HttpResult<UserProfile>.NotFound(); } return HttpResult<UserProfile>.Ok(userProfile); } var profileResponse = GetUserProfile(1); if (profileResponse.IsOk) { Console.WriteLine($"User Profile: {profileResponse.Value}"); } else if (profileResponse.WasNotFound) { Console.WriteLine("User profile not found."); }
Handling an Unauthorized Access:
HttpResult<string> GetSecretData(string authToken) { if (!IsAuthorized(authToken)) { return HttpResult<string>.Unauthorized(); } string secretData = FetchSecretData(); // Assume this fetches data return HttpResult<string>.Ok(secretData); } var secretDataResponse = GetSecretData("someAuthToken"); if (secretDataResponse.IsUnauthorized) { Console.WriteLine("Unauthorized access."); }
- SignInResult.cs: Designed for authentication processes.
The SignInResult
class is designed to encapsulate the outcome of authentication operations, providing specific statuses like Locked
, Blocked
, Failed
, etc. Here are some examples:
Examples:
Successful Sign-In:
SignInResult AuthenticateUser(string username, string password) { // Logic to authenticate user if (isAuthenticated) { string token = GenerateToken(user); // Assume GenerateToken generates a token return new SignInResult(token); } return SignInResult.Failed(); } var signInResult = AuthenticateUser("user1", "password123"); if (signInResult.IsSuccess) { Console.WriteLine($"Authentication successful. Token: {signInResult.Token}"); } else { Console.WriteLine("Authentication failed."); }
Handling Locked Account:
SignInResult CheckAccountStatus(User user) { if (user.IsLocked) { return SignInResult.Locked(); } // Further checks } var accountStatus = CheckAccountStatus(someUser); if (accountStatus.IsLocked) { Console.WriteLine("Account is locked."); }
Two-Factor Authentication Required:
SignInResult PerformTwoFactorCheck(User user) { if (user.RequiresTwoFactor) { return SignInResult.TwoFactorRequired(); } return SignInResult.Success(GenerateToken(user)); // Success with token } var twoFactorResult = PerformTwoFactorCheck(someUser); if (twoFactorResult.RequiresTwoFactor) { Console.WriteLine("Two-factor authentication required."); }
System Utilities
System utilities provide abstractions over system resources like Date and Time, GUIDs, File System, and Console Input/Output. These abstractions are instrumental in creating testable code by enabling dependency injection, which allows for the substitution of these system classes with mock objects during testing.
- DateTimeProvider.cs: Facilitates working with dates and times in a testable way by abstracting system-specific implementations.
Examples:
Injecting DateTimeProvider into a service for testability:
public class TimeSensitiveService { private readonly DateTimeProvider _dateTimeProvider; public TimeSensitiveService(DateTimeProvider dateTimeProvider) { _dateTimeProvider = dateTimeProvider; } public bool IsBusinessHour() { var currentHour = _dateTimeProvider.Now.Hour; return currentHour >= 9 && currentHour < 17; } }
Mocking DateTimeProvider for unit tests:
var fixedDateTime = Substitute.For<DateTimeProvider>(); fixedDateTime.Now.Returns(new DateTimeOffset(2023, 1, 1, 12, 0, 0, TimeSpan.Zero)); var service = new TimeSensitiveService(fixedDateTime); var result = service.IsBusinessHour(); result.Should().BeTrue(); // FluentAssertions used here
- GuidProvider.cs: Allows generation of GUIDs that can be controlled in a testing environment.
Examples:
Using GuidProvider to generate GUIDs in a service:
public class IdentifierService { private readonly GuidProvider _guidProvider; public IdentifierService(GuidProvider guidProvider) { _guidProvider = guidProvider; } public Guid GetUniqueIdentifier() { return _guidProvider.New(); } }
Mocking GuidProvider in tests:
var expectedGuid = Guid.NewGuid(); var fixedGuidProvider = Substitute.For<GuidProvider>(); fixedGuidProvider.New().Returns(expectedGuid); var service = new IdentifierService(fixedGuidProvider); var id = service.GetUniqueIdentifier(); id.Should().Be(expectedGuid); // Asserting that the ID is indeed a GUID
- FileSystem.cs: Provides an abstraction over file system operations, enabling better testing of file-related functionality.
Examples:
Injecting FileSystem into a component:
public class FileManager { private readonly FileSystem _fileSystem; public FileManager(FileSystem fileSystem) { _fileSystem = fileSystem; } public bool FileExists(string path) { return _fileSystem.FileExists(path); } }
Mocking FileSystem during testing:
var mockedFileSystem = Substitute.For<FileSystem>(); mockedFileSystem.FileExists("test.txt").Returns(true); var fileManager = new FileManager(mockedFileSystem); var exists = fileManager.FileExists("test.txt"); exists.Should().BeTrue(); // Validate the expected behavior
- Input.cs, Output.cs: Utilities for input and output operations, making console interactions testable.
Examples:
Using Output in a service to write messages:
public class ConsoleService { private readonly Input _input; private readonly Output _output; public ConsoleService(Input input, Output output) { _input = input; _output = output; } public string ReadUserInputOrEmpty() { return _input.ReadLine() ?? string.Empty; } public void DisplayMessage(string target) { _output.WriteLine($"Hello {target}!"); } }
Testing console input and output interactions:
var mockedOutput = Substitute.For<Output>(); var mockedInput = Substitute.For<Input>(); var consoleService = new ConsoleService(mockedInput, mockedOutput); mockedInput.ReadLine().Returns("Test") var target = consoleService.ReadUserInputOrEmpty(); var consoleService.DisplayMessage(target); outputMock.Received().WriteLine("Hello Test!"); // NSubstitute used to verify interaction
Pagination Utilities
Pagination utilities provide a standardized way to handle the slicing of large datasets into smaller, more manageable blocks or pages. These utilities can be used to define and access specific segments of data, often necessary when implementing APIs that support pagination.
IBlock / Block: Interfaces and classes that represent a contiguous block of items with a specific size and an offset that indicates the starting point or key item.
IPage / Page: Extends the concept of a block to include pagination-specific information, such as the total count of items, allowing for the calculation of the total number of pages.
BlockSettings: Provides constants related to pagination, such as default block size and maximum allowable values.
Examples:
Creating a Block of items:
var items = new List<string> { "Item1", "Item2", "Item3" }; var block = new Block<string>(items, "Item1"); // block now contains a block of items starting from "Item1"
Creating a Page with total count information:
var items = new List<string> { "Item1", "Item2", "Item3" }; var page = new Page<string>(items, 0, BlockSettings.DefaultBlockSize, 100); // page now contains a page of items with information about the total count of items
Singleton and Options Patterns
Pattern utilities offer standardized ways to define and access special instances of classes, such as singleton, default, or empty instances. These utilities are designed to implement common design patterns in a consistent manner across your applications.
IHasInstance / HasInstance: Allows for defining and accessing a singleton instance of a class.
IHasDefault / HasDefault: Provides a way to access a default instance of a class.
IHasEmpty / HasEmpty: Enables defining an 'empty' instance, often representing the absence of a value.
INamedOptions / NamedOptions: Used for options classes with a standard way to define the section name used for configuration binding.
Examples:
Accessing a singleton instance:
public class SingletonService : HasInstance<SingletonService> { // Service implementation } var instance = SingletonService.Instance;
Defining default options:
public class ServiceOptions : NamedOptions<ServiceOptions> { public string ConnectionString { get; set; } = "DefaultConnectionString"; } var defaultOptions = ServiceOptions.Default;
Here is an example of the options class being used in a configuration binding scenario:
// In appsettings.json { // ... "Service": { "ConnectionString": "SomeConnectionString" } // ... }
public class Service { private readonly ServiceOptions _options; public Service(IOptions<ServiceOptions> options) { _options = options.Value; } public string GetConnectionString() { return _options.ConnectionString; } } // In Startup.cs services.Configure<ServiceOptions>(Configuration.GetSection("Service"));
Using an empty instance:
public class CommandResult : HasEmpty<CommandResult> { public bool Success { get; set; } } var noResult = CommandResult.Empty;
Extension Classes
Extension classes provide additional methods to existing types, enhancing their capabilities and simplifying common operations.
- EnumerableExtensions.cs: Extends
IEnumerable<T>
to provide additional transformation and collection generation methods.
Examples:
Transforming IEnumerable to Array with custom projection:
IEnumerable<int> numbers = new[] { 1, 2, 3, 4 }; int[] squaredNumbers = numbers.ToArray(x => x * x); // squaredNumbers would be [1, 4, 9, 16]
Creating a Dictionary from another IDictionary with a value transformation:
IDictionary<string, int>> original = new Dictionary<string, int> { ["one"] = 1, ["two"] = 2 }; var newDictionary = original.ToDictionary(v => v * 10); // Multiplies each value by 10 // newDictionary would have values [10, 20]
- QueryableExtensions.cs: Provides projection and conversion extensions for
IQueryable<T>
.
Examples:
Projecting IQueryable to Array:
IQueryable<Person> people = GetPeopleQuery(); PersonDto[] dtos = people.ToArray(person => new PersonDto(person.Name, person.Age)); // dtos contains PersonDto objects projected from the Person query
Transforming IQueryable into HashSet with projection:
IQueryable<string> names = GetNamesQuery(); HashSet<string> upperCaseNames = names.ToHashSet(name => name.ToUpper()); // upperCaseNames contains upper case versions of the names from the query
- TaskExtensions.cs: Adds methods to
Task
andValueTask
to handle fire-and-forget scenarios with optional exception handling callbacks.
Examples:
Using FireAndForget with a Task:
Task someTask = DoWorkAsync(); someTask.FireAndForget(); // No need to await, exceptions will be ignored
Handling exceptions with FireAndForget:
async Task DoWorkAsync() { // Simulate work await Task.Delay(1000); throw new InvalidOperationException("Something went wrong"); } Task anotherTask = DoWorkAsync(); anotherTask.FireAndForget( onException: ex => LogError(ex) // Log error if exception occurs );
Other Utilities
Other utilities provide additional functionalities and helpers to extend existing concepts, making them more convenient and robust for everyday use.
- Ensure.cs: Offers a variety of methods to ensure arguments meet certain conditions, throwing exceptions if they do not. This utility is very useful for validating method inputs and maintaining contracts within the code.
You can add the
Ensure
class as a static using to simplify the call.
Examples:
Using Ensure to validate arguments:
public void ProcessData(string input) { input = Ensure.IsNotNullOrWhiteSpace(input); // Rest of the method logic }
Using Ensure as a static using:
using static DotNetToolbox.Ensure; // class dedinition and other code public void ProcessList(IList<string> items) { items = HasNoNullOrWhiteSpace(items); // Rest of the method logic }
- CreateInstance.cs: Provides methods to dynamically create instances of types, which can be very helpful for creating types with non-public constructors or when types need to be created based on runtime data.
Examples:
Creating an instance of a class with private constructor:
var instance1 = CreateInstance.Of<SomeClassWithPrivateConstructor>(); var instance2 = CreateInstance.Of<SomeClassWithArguments>(arg1, arg2);
Creating an instance using dependency injection:
public class SomeClassWithArgumentsAndServices { public SomeClassWithArgumentsAndServices(string arg1, string arg2, ILoggerFactory loggerFactory) { // ... } } // ... var serviceProvider = // ... build your service provider var instance = CreateInstance.Of<SomeClassWithArgumentsAndServices>(serviceProvider, arg1, arg2); // the injected services do not need to be added.
- UrlSafeBase64String: A record struct that represents a base64 encoded string safe for URL use. It can be used to encode and decode bytes, strings, and GUIDs to a URL-safe base64 format.
Examples:
Encoding and decoding a URL-safe base64 string:
byte[] original = { 0x1, 0x2, 0x3 }; UrlSafeBase64String encoded = original; byte[] decoded = encoded; encoded.Should().BeOfType<string>(); // Outputs URL-safe base64 string decoded.Should().BeEquivalentTo(original); // Outputs True
Working with GUIDs and base64 strings:
Guid original = Guid.NewGuid(); UrlSafeBase64String encoded = original; Guid decoded = encoded; encoded.Should().BeOfType<string>(); // Outputs URL-safe base64 string decoded.Should().Be(original); // Outputs True
- IValidatable: An interface that classes can implement to provide custom validation logic, returning a
Result
that indicates whether the validation was successful or not.
Examples:
Implementing IValidatable in a class:
using static DotNetToolbox.Result; public class Person : IValidatable { public string? Name { get; set; } public int Age { get; set; } public Result Validate(IDictionary<string, object?>? context = null) { var result = Success(); if (string.IsNullOrWhiteSpace(Name)) { result += InvalidData(nameof(Name), "Name cannot be empty or whitespace."); } if (Age < 0 || Age > 120) { result += InvalidData(nameof(Age), "Age must be between 0 and 120."); } return result; // may return multiple errors } }
Using Ensure to validate an IValidatable object:
using static DotNetToolbox.Ensure; public void ProcessPerson(Person person) { person = IsValid(person); // throws ValidationException if validation fails. // Rest of the method logic }
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
NuGet packages (8)
Showing the top 5 NuGet packages that depend on DotNetToolbox.Core:
Package | Downloads |
---|---|
DotNetToolbox.Console
A library with powerful tools to simplify the creation of CLI (Command-Line Interface) and Shell-like console applications. |
|
DotNetToolbox.Http
DotNetToolbox.Http: A comprehensive utility library for handling http connections and more. |
|
DotNetToolbox.Azure
DotNetToolbox.Azure: A comprehensive utility library for handling Azure assets. |
|
DotNetToolbox.Security
DotNetToolbox.Security: A comprehensive utility library for hashing, encryption and decryption. |
|
DotNetToolbox.ValidationBuilder
DotNetToolbox.ValidationBuilder: A comprehensive utility library for building object validation and more. |
GitHub repositories
This package is not used by any popular GitHub repositories.
DotNetToolbox.Core Version 8.0.3
Stable release of the DotNetToolbox.Core library, a comprehensive suite of utilities and patterns designed to enhance .NET 8 development. This library provides developers with a robust set of tools to simplify complex tasks, improve testability, and streamline everyday coding activities.
What's new:
- Minor bug corrections.