(feature): sagas Ilogger improvements, documentation

This commit is contained in:
Maksym Sadovnychyy 2025-10-31 21:18:28 +01:00
parent 224b38b408
commit cb83909ba2
14 changed files with 537 additions and 2988 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2024 Maksym Sadovnychyy (MAKS-IT) Copyright (c) 2024 - 2025 Maksym Sadovnychyy (MAKS-IT)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

2536
README.md

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ namespace MaksIT.Core.Extensions.Tests;
string outputTarPath = "output.tar"; string outputTarPath = "output.tar";
// Act // Act
bool result = FromatsExtensions.TryCreateTarFromDirectory(invalidSourceDirectory, outputTarPath); bool result = FormatsExtensions.TryCreateTarFromDirectory(invalidSourceDirectory, outputTarPath);
// Assert // Assert
Assert.False(result); Assert.False(result);
@ -23,7 +23,7 @@ namespace MaksIT.Core.Extensions.Tests;
string invalidOutputPath = ""; string invalidOutputPath = "";
// Act // Act
bool result = FromatsExtensions.TryCreateTarFromDirectory(sourceDirectory, invalidOutputPath); bool result = FormatsExtensions.TryCreateTarFromDirectory(sourceDirectory, invalidOutputPath);
// Assert // Assert
Assert.False(result); Assert.False(result);
@ -37,7 +37,7 @@ namespace MaksIT.Core.Extensions.Tests;
string outputTarPath = Path.Combine(Path.GetTempPath(), "output.tar"); string outputTarPath = Path.Combine(Path.GetTempPath(), "output.tar");
// Act // Act
bool result = FromatsExtensions.TryCreateTarFromDirectory(sourceDirectory, outputTarPath); bool result = FormatsExtensions.TryCreateTarFromDirectory(sourceDirectory, outputTarPath);
// Assert // Assert
Assert.False(result); Assert.False(result);
@ -57,7 +57,7 @@ namespace MaksIT.Core.Extensions.Tests;
string outputTarPath = Path.Combine(Path.GetTempPath(), "output.tar"); string outputTarPath = Path.Combine(Path.GetTempPath(), "output.tar");
// Act // Act
bool result = FromatsExtensions.TryCreateTarFromDirectory(sourceDirectory, outputTarPath); bool result = FormatsExtensions.TryCreateTarFromDirectory(sourceDirectory, outputTarPath);
// Assert // Assert
Assert.True(result); Assert.True(result);
@ -82,7 +82,7 @@ namespace MaksIT.Core.Extensions.Tests;
// Lock the file to simulate inability to create it // Lock the file to simulate inability to create it
using (FileStream lockedFile = File.Create(outputTarPath)) { using (FileStream lockedFile = File.Create(outputTarPath)) {
// Act // Act
bool result = FromatsExtensions.TryCreateTarFromDirectory(sourceDirectory, outputTarPath); bool result = FormatsExtensions.TryCreateTarFromDirectory(sourceDirectory, outputTarPath);
// Assert // Assert
Assert.False(result); Assert.False(result);

View File

@ -11,7 +11,7 @@ public class LocalSagaTests
{ {
// Arrange // Arrange
var logger = LoggerHelper.CreateConsoleLogger(); var logger = LoggerHelper.CreateConsoleLogger();
var builder = new LocalSagaBuilder().WithLogger(logger); var builder = new LocalSagaBuilder(logger);
var stepExecuted = false; var stepExecuted = false;
builder.AddAction( builder.AddAction(
@ -36,7 +36,7 @@ public class LocalSagaTests
{ {
// Arrange // Arrange
var logger = LoggerHelper.CreateConsoleLogger(); var logger = LoggerHelper.CreateConsoleLogger();
var builder = new LocalSagaBuilder().WithLogger(logger); var builder = new LocalSagaBuilder(logger);
var compensationCalled = false; var compensationCalled = false;
builder.AddAction( builder.AddAction(
@ -63,7 +63,7 @@ public class LocalSagaTests
{ {
// Arrange // Arrange
var logger = LoggerHelper.CreateConsoleLogger(); var logger = LoggerHelper.CreateConsoleLogger();
var builder = new LocalSagaBuilder().WithLogger(logger); var builder = new LocalSagaBuilder(logger);
var stepExecuted = false; var stepExecuted = false;
builder.AddActionIf( builder.AddActionIf(
@ -89,7 +89,7 @@ public class LocalSagaTests
{ {
// Arrange // Arrange
var logger = LoggerHelper.CreateConsoleLogger(); var logger = LoggerHelper.CreateConsoleLogger();
var builder = new LocalSagaBuilder().WithLogger(logger); var builder = new LocalSagaBuilder(logger);
builder.AddAction( builder.AddAction(
"LoggingStep", "LoggingStep",
@ -107,7 +107,7 @@ public class LocalSagaTests
{ {
// Arrange // Arrange
var logger = LoggerHelper.CreateConsoleLogger(); var logger = LoggerHelper.CreateConsoleLogger();
var builder = new LocalSagaBuilder().WithLogger(logger); var builder = new LocalSagaBuilder(logger);
var context = new LocalSagaContext(); var context = new LocalSagaContext();
context.Set("state", "initial"); context.Set("state", "initial");
@ -143,7 +143,7 @@ public class LocalSagaTests
{ {
// Arrange // Arrange
var logger = LoggerHelper.CreateConsoleLogger(); var logger = LoggerHelper.CreateConsoleLogger();
var builder = new LocalSagaBuilder().WithLogger(logger); var builder = new LocalSagaBuilder(logger);
var context = new LocalSagaContext(); var context = new LocalSagaContext();
var compensationLog = new List<string>(); var compensationLog = new List<string>();

View File

@ -4,12 +4,13 @@
namespace MaksIT.Core.Extensions; namespace MaksIT.Core.Extensions;
public static class DataTableExtensions { public static class DataTableExtensions {
/// <summary>
/// Counts duplicate records between two DataTables.
/// </summary>
/// <param name="dt1"></param>
/// <param name="dt2"></param>
/// <returns></returns>
public static int DuplicatesCount(this DataTable dt1, DataTable dt2) { public static int DuplicatesCount(this DataTable dt1, DataTable dt2) {
if (dt1 == null)
throw new ArgumentNullException(nameof(dt1));
if (dt2 == null)
throw new ArgumentNullException(nameof(dt2));
var duplicates = 0; var duplicates = 0;
foreach (DataRow dtRow1 in dt1.Rows) { foreach (DataRow dtRow1 in dt1.Rows) {
var dt1Items = dtRow1.ItemArray.Select(item => item?.ToString() ?? string.Empty); var dt1Items = dtRow1.ItemArray.Select(item => item?.ToString() ?? string.Empty);
@ -34,11 +35,6 @@ public static class DataTableExtensions {
/// <param name="columns"></param> /// <param name="columns"></param>
/// <returns></returns> /// <returns></returns>
public static DataTable DistinctRecords(this DataTable dt, string[] columns) { public static DataTable DistinctRecords(this DataTable dt, string[] columns) {
if (dt == null)
throw new ArgumentNullException(nameof(dt));
if (columns == null)
throw new ArgumentNullException(nameof(columns));
return dt.DefaultView.ToTable(true, columns); return dt.DefaultView.ToTable(true, columns);
} }
} }

View File

@ -3,6 +3,14 @@
namespace MaksIT.Core.Extensions; namespace MaksIT.Core.Extensions;
public static class DateTimeExtensions { public static class DateTimeExtensions {
/// <summary>
/// Adds workdays to a given date, skipping weekends and holidays defined in the holiday calendar.
/// </summary>
/// <param name="date"></param>
/// <param name="days"></param>
/// <param name="holidayCalendar"></param>
/// <returns></returns>
public static DateTime AddWorkdays(this DateTime date, int days, IHolidayCalendar holidayCalendar) { public static DateTime AddWorkdays(this DateTime date, int days, IHolidayCalendar holidayCalendar) {
if (days == 0) if (days == 0)
return date; return date;
@ -28,21 +36,45 @@ public static class DateTimeExtensions {
return currentDate; return currentDate;
} }
/// <summary>
/// Adds workdays to a given date, skipping weekends and holidays defined in the holiday calendar.
/// </summary>
/// <param name="date"></param>
/// <param name="timeSpanWorkDays"></param>
/// <param name="holidayCalendar"></param>
/// <returns></returns>
public static DateTime AddWorkdays(this DateTime date, TimeSpan timeSpanWorkDays, IHolidayCalendar holidayCalendar) => public static DateTime AddWorkdays(this DateTime date, TimeSpan timeSpanWorkDays, IHolidayCalendar holidayCalendar) =>
date.AddWorkdays(timeSpanWorkDays.Days, holidayCalendar); date.AddWorkdays(timeSpanWorkDays.Days, holidayCalendar);
/// <summary>
/// Finds the next specified weekday from the given start date.
/// </summary>
/// <param name="start"></param>
/// <param name="day"></param>
/// <returns></returns>
public static DateTime NextWeekday(this DateTime start, DayOfWeek day) { public static DateTime NextWeekday(this DateTime start, DayOfWeek day) {
int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7; int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7;
daysToAdd = daysToAdd == 0 ? 7 : daysToAdd; // If today is the target day, move to next week daysToAdd = daysToAdd == 0 ? 7 : daysToAdd; // If today is the target day, move to next week
return start.AddDays(daysToAdd); return start.AddDays(daysToAdd);
} }
/// <summary>
/// Finds the TimeSpan to the next specified weekday from the given start date.
/// </summary>
/// <param name="start"></param>
/// <param name="day"></param>
/// <returns></returns>
public static TimeSpan ToNextWeekday(this DateTime start, DayOfWeek day) { public static TimeSpan ToNextWeekday(this DateTime start, DayOfWeek day) {
int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7; int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7;
daysToAdd = daysToAdd == 0 ? 7 : daysToAdd; daysToAdd = daysToAdd == 0 ? 7 : daysToAdd;
return TimeSpan.FromDays(daysToAdd); return TimeSpan.FromDays(daysToAdd);
} }
/// <summary>
/// Finds the next end of month from the given date.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static DateTime NextEndOfMonth(this DateTime date) { public static DateTime NextEndOfMonth(this DateTime date) {
var nextMonth = date.AddMonths(1); var nextMonth = date.AddMonths(1);
return new DateTime( return new DateTime(
@ -55,6 +87,11 @@ public static class DateTimeExtensions {
date.Kind); date.Kind);
} }
/// <summary>
/// Finds the end of month for the given date.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static DateTime EndOfMonth(this DateTime date) => public static DateTime EndOfMonth(this DateTime date) =>
new DateTime( new DateTime(
date.Year, date.Year,
@ -65,29 +102,70 @@ public static class DateTimeExtensions {
date.Second, date.Second,
date.Kind); date.Kind);
/// <summary>
/// Finds the begin of month for the given date.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static DateTime BeginOfMonth(this DateTime date) => public static DateTime BeginOfMonth(this DateTime date) =>
new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind); new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind);
/// <summary>
/// Finds the start of year for the given date.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static DateTime StartOfYear(this DateTime date) => public static DateTime StartOfYear(this DateTime date) =>
new DateTime(date.Year, 1, 1, date.Hour, date.Minute, date.Second, date.Kind); new DateTime(date.Year, 1, 1, date.Hour, date.Minute, date.Second, date.Kind);
/// <summary>
/// Finds the end of year for the given date.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static DateTime EndOfYear(this DateTime date) => public static DateTime EndOfYear(this DateTime date) =>
new DateTime(date.Year, 12, 31, date.Hour, date.Minute, date.Second, date.Kind); new DateTime(date.Year, 12, 31, date.Hour, date.Minute, date.Second, date.Kind);
/// <summary>
/// Determines if the given date is the end of the month.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static bool IsEndOfMonth(this DateTime date) => public static bool IsEndOfMonth(this DateTime date) =>
date.Day == DateTime.DaysInMonth(date.Year, date.Month); date.Day == DateTime.DaysInMonth(date.Year, date.Month);
/// <summary>
/// Determines if the given date is the begin of the month.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static bool IsBeginOfMonth(this DateTime date) => date.Day == 1; public static bool IsBeginOfMonth(this DateTime date) => date.Day == 1;
/// <summary>
/// Determines if the given date is the end of the year.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static bool IsEndOfYear(this DateTime date) => date.Day == 31 && date.Month == 12; public static bool IsEndOfYear(this DateTime date) => date.Day == 31 && date.Month == 12;
/// <summary>
/// Determines if the given date is the begin of the year.
/// </summary>
/// <param name="date"></param>
/// <param name="targetDate"></param>
/// <returns></returns>
public static bool IsSameMonth(this DateTime date, DateTime targetDate) => public static bool IsSameMonth(this DateTime date, DateTime targetDate) =>
date.Month == targetDate.Month && date.Year == targetDate.Year; date.Month == targetDate.Month && date.Year == targetDate.Year;
/// <summary>
/// Calculates the difference in complete years between two dates.
/// Implementation follows the logic used in Excel's DATEDIF function
/// "COMPLETE calendar years in between dates"
/// </summary>
/// <param name="startDate"></param>
/// <param name="endDate"></param>
/// <returns></returns>
public static int GetDifferenceInYears(this DateTime startDate, DateTime endDate) { public static int GetDifferenceInYears(this DateTime startDate, DateTime endDate) {
// Implementation follows the logic used in Excel's DATEDIF function
// "COMPLETE calendar years in between dates"
int years = endDate.Year - startDate.Year; int years = endDate.Year - startDate.Year;
if (endDate.Month < startDate.Month || if (endDate.Month < startDate.Month ||

View File

@ -2,7 +2,7 @@
namespace MaksIT.Core.Extensions; namespace MaksIT.Core.Extensions;
public static class FromatsExtensions { public static class FormatsExtensions {
public static bool TryCreateTarFromDirectory(string sourceDirectory, string outputTarPath) { public static bool TryCreateTarFromDirectory(string sourceDirectory, string outputTarPath) {
// Validate source directory // Validate source directory
if (string.IsNullOrWhiteSpace(sourceDirectory) || !Directory.Exists(sourceDirectory)) { if (string.IsNullOrWhiteSpace(sourceDirectory) || !Directory.Exists(sourceDirectory)) {

View File

@ -8,7 +8,7 @@
<!-- NuGet package metadata --> <!-- NuGet package metadata -->
<PackageId>MaksIT.Core</PackageId> <PackageId>MaksIT.Core</PackageId>
<Version>1.4.8</Version> <Version>1.4.9</Version>
<Authors>Maksym Sadovnychyy</Authors> <Authors>Maksym Sadovnychyy</Authors>
<Company>MAKS-IT</Company> <Company>MAKS-IT</Company>
<Product>MaksIT.Core</Product> <Product>MaksIT.Core</Product>

View File

@ -1,9 +1,5 @@
using System; using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MaksIT.Core.Sagas; namespace MaksIT.Core.Sagas;
/// <summary> /// <summary>
@ -15,7 +11,8 @@ public sealed class LocalSaga {
internal LocalSaga( internal LocalSaga(
IReadOnlyList<ILocalSagaStep> pipeline, IReadOnlyList<ILocalSagaStep> pipeline,
ILogger logger) { ILogger logger
) {
_pipeline = pipeline; _pipeline = pipeline;
_logger = logger; _logger = logger;
} }

View File

@ -1,9 +1,5 @@
using System; using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MaksIT.Core.Sagas; namespace MaksIT.Core.Sagas;
@ -12,11 +8,10 @@ namespace MaksIT.Core.Sagas;
/// </summary> /// </summary>
public sealed class LocalSagaBuilder { public sealed class LocalSagaBuilder {
private readonly List<ILocalSagaStep> _pipeline = new(); private readonly List<ILocalSagaStep> _pipeline = new();
private ILogger? _logger; private ILogger _logger;
public LocalSagaBuilder WithLogger(ILogger logger) { public LocalSagaBuilder (ILogger logger) {
_logger = logger; _logger = logger;
return this;
} }
public LocalSagaBuilder AddAction( public LocalSagaBuilder AddAction(
@ -66,8 +61,6 @@ public sealed class LocalSagaBuilder {
} }
public LocalSaga Build() { public LocalSaga Build() {
if (_logger == null)
throw new InvalidOperationException("Logger must be provided via WithLogger().");
return new LocalSaga(_pipeline, _logger); return new LocalSaga(_pipeline, _logger);
} }
} }

View File

@ -1,10 +1,4 @@
using System; namespace MaksIT.Core.Sagas;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MaksIT.Core.Sagas;
/// <summary> /// <summary>
/// Shared context to pass values between steps without tight coupling. /// Shared context to pass values between steps without tight coupling.

View File

@ -1,11 +1,4 @@
using System; namespace MaksIT.Core.Sagas;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MaksIT.Core.Sagas;
/// <summary> /// <summary>
/// Internal non-generic step interface to unify generic steps. /// Internal non-generic step interface to unify generic steps.

View File

@ -1,10 +1,5 @@
using System; namespace MaksIT.Core.Sagas;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MaksIT.Core.Sagas;
/// <summary> /// <summary>
/// A simple unit type for steps that do not return a value. /// A simple unit type for steps that do not return a value.
/// </summary> /// </summary>

View File

@ -1,811 +0,0 @@
# MaksIT.Core Library Documentation
## Table of Contents
- [Abstractions](#abstractions)
- [Base Classes](#base-classes)
- [Enumeration](#enumeration)
- [Extensions](#extensions)
- [Expression Extensions](#expression-extensions)
- [DateTime Extensions](#datetime-extensions)
- [String Extensions](#string-extensions)
- [Object Extensions](#object-extensions)
- [DataTable Extensions](#datatable-extensions)
- [Guid Extensions](#guid-extensions)
- [Logging](#logging)
- [Networking](#networking)
- [Network Connection](#network-connection)
- [Ping Port](#ping-port)
- [Security](#security)
- [AES-GCM Utility](#aes-gcm-utility)
- [Base32 Encoder](#base32-encoder)
- [Checksum Utility](#checksum-utility)
- [Password Hasher](#password-hasher)
- [JWT Generator](#jwt-generator)
- [TOTP Generator](#totp-generator)
- [Web API Models](#web-api-models)
- [Sagas](#sagas)
- [Others](#others)
- [Culture](#culture)
- [Environment Variables](#environment-variables)
- [File System](#file-system)
- [Processes](#processes)
## Abstractions
### Base Classes
The following base classes in the `MaksIT.Core.Abstractions` namespace provide a foundation for implementing domain, DTO, and Web API models, ensuring consistency and maintainability in application design.
---
##### 1. **`DomainObjectBase`**
###### Summary
Represents the base class for all domain objects in the application.
###### Purpose
- Serves as the foundation for all domain objects.
- Provides a place to include shared logic or properties for domain-level entities in the future.
---
##### 2. **`DomainDocumentBase<T>`**
###### Summary
Represents a base class for domain documents with a unique identifier.
###### Purpose
- Extends `DomainObjectBase` to include an identifier.
- Provides a common structure for domain entities that need unique IDs.
###### Example Usage
```csharp
public class UserDomainDocument : DomainDocumentBase<Guid> {
public UserDomainDocument(Guid id) : base(id) {
}
}
```
---
##### 3. **`DtoObjectBase`**
###### Summary
Represents the base class for all Data Transfer Objects (DTOs).
###### Purpose
- Serves as the foundation for all DTOs.
- Provides a place to include shared logic or properties for DTOs in the future.
---
##### 4. **`DtoDocumentBase<T>`**
###### Summary
Represents a base class for DTOs with a unique identifier.
###### Purpose
- Extends `DtoObjectBase` to include an identifier.
- Provides a common structure for DTOs that need unique IDs.
###### Example Usage
```csharp
public class UserDto : DtoDocumentBase<Guid> {
public required string Name { get; set; }
}
```
---
##### 5. **`RequestModelBase`**
###### Summary
Represents the base class for Web API request models.
###### Purpose
- Serves as a foundation for request models used in Web API endpoints.
- Provides a common structure for request validation or shared properties.
###### Example Usage
```csharp
public class CreateUserRequest : RequestModelBase {
public required string Name { get; set; }
}
```
---
##### 6. **`ResponseModelBase`**
###### Summary
Represents the base class for Web API response models.
###### Purpose
- Serves as a foundation for response models returned by Web API endpoints.
- Provides a common structure for standardizing API responses.
###### Example Usage
```csharp
public class UserResponse : ResponseModelBase {
public required Guid Id { get; set; }
public required string Name { get; set; }
}
```
---
#### Features and Benefits
1. **Consistency**:
- Ensures a uniform structure for domain, DTO, and Web API models.
2. **Extensibility**:
- Base classes can be extended to include shared properties or methods as needed.
3. **Type Safety**:
- Generic identifiers (`T`) ensure type safety for domain documents and DTOs.
4. **Reusability**:
- Common logic or properties can be added to base classes and reused across the application.
---
#### Example End-to-End Usage
```csharp
// Domain Class
public class ProductDomain : DomainDocumentBase<int> {
public ProductDomain(int id) : base(id) { }
public string Name { get; set; } = string.Empty;
}
// DTO Class
public class ProductDto : DtoDocumentBase<int> {
public required string Name { get; set; }
}
// Web API Request Model
public class CreateProductRequest : RequestModelBase {
public required string Name { get; set; }
}
// Web API Response Model
public class ProductResponse : ResponseModelBase {
public required int Id { get; set; }
public required string Name { get; set; }
}
```
---
#### Best Practices
1. **Keep Base Classes Lightweight**:
- Avoid adding unnecessary properties or methods to base classes.
2. **Encapsulation**:
- Use base classes to enforce encapsulation and shared behavior across entities.
3. **Validation**:
- Extend `RequestModelBase` or `ResponseModelBase` to include validation logic if needed.
---
This structure promotes clean code principles, reducing redundancy and improving maintainability across the application layers.
---
### Enumeration
The `Enumeration` class in the `MaksIT.Core.Abstractions` namespace provides a base class for creating strongly-typed enumerations. It enables you to define enumerable constants with additional functionality, such as methods for querying, comparing, and parsing enumerations.
---
#### Features and Benefits
1. **Strongly-Typed Enumerations**:
- Combines the clarity of enums with the extensibility of classes.
- Supports additional fields, methods, or logic as needed.
2. **Reflection Support**:
- Dynamically retrieve all enumeration values with `GetAll`.
3. **Parsing Capabilities**:
- Retrieve enumeration values by ID or display name.
4. **Comparison and Equality**:
- Fully implements equality and comparison operators for use in collections and sorting.
---
#### Example Usage
#### Defining an Enumeration
```csharp
public class MyEnumeration : Enumeration {
public static readonly MyEnumeration Value1 = new(1, "Value One");
public static readonly MyEnumeration Value2 = new(2, "Value Two");
private MyEnumeration(int id, string name) : base(id, name) { }
}
```
#### Retrieving All Values
```csharp
var allValues = Enumeration.GetAll<MyEnumeration>();
allValues.ToList().ForEach(Console.WriteLine);
```
#### Parsing by ID or Name
```csharp
var valueById = Enumeration.FromValue<MyEnumeration>(1);
var valueByName = Enumeration.FromDisplayName<MyEnumeration>("Value One");
Console.WriteLine(valueById); // Output: Value One
Console.WriteLine(valueByName); // Output: Value One
```
#### Comparing Enumeration Values
```csharp
var difference = Enumeration.AbsoluteDifference(MyEnumeration.Value1, MyEnumeration.Value2);
Console.WriteLine($"Absolute Difference: {difference}"); // Output: 1
```
#### Using in Collections
```csharp
var values = new List<MyEnumeration> { MyEnumeration.Value2, MyEnumeration.Value1 };
values.Sort(); // Orders by ID
```
---
#### Best Practices
1. **Extend for Specific Enums**:
- Create specific subclasses for each enumeration type.
2. **Avoid Duplicates**:
- Ensure unique IDs and names for each enumeration value.
3. **Use Reflection Sparingly**:
- Avoid calling `GetAll` in performance-critical paths.
---
The `Enumeration` class provides a powerful alternative to traditional enums, offering flexibility and functionality for scenarios requiring additional metadata or logic.
---
### Extensions
### Guid Extensions
The `GuidExtensions` class provides methods for working with `Guid` values, including converting them to nullable types.
---
#### Features
1. **Convert to Nullable**:
- Convert a `Guid` to a nullable `Guid?`, returning `null` if the `Guid` is empty.
---
#### Example Usage
##### Converting to Nullable
```csharp
Guid id = Guid.NewGuid();
Guid? nullableId = id.ToNullable();
```
---
### Expression Extensions
The `ExpressionExtensions` class provides utility methods for combining and manipulating LINQ expressions. These methods are particularly useful for building dynamic queries in a type-safe manner.
---
#### Features
1. **Combine Expressions**:
- Combine two expressions using logical operators like `AndAlso` and `OrElse`.
2. **Negate Expressions**:
- Negate an expression using the `Not` method.
3. **Batch Processing**:
- Divide a collection into smaller batches for processing.
---
#### Example Usage
##### Combining Expressions
```csharp
Expression<Func<int, bool>> isEven = x => x % 2 == 0;
Expression<Func<int, bool>> isPositive = x => x > 0;
var combined = isEven.AndAlso(isPositive);
var result = combined.Compile()(4); // True
```
##### Negating Expressions
```csharp
Expression<Func<int, bool>> isEven = x => x % 2 == 0;
var notEven = isEven.Not();
var result = notEven.Compile()(3); // True
```
---
### DateTime Extensions
The `DateTimeExtensions` class provides methods for manipulating and querying `DateTime` objects. These methods simplify common date-related operations.
---
#### Features
1. **Add Workdays**:
- Add a specified number of workdays to a date, excluding weekends and holidays.
2. **Find Specific Dates**:
- Find the next occurrence of a specific day of the week.
3. **Month and Year Boundaries**:
- Get the start or end of the current month or year.
---
#### Example Usage
##### Adding Workdays
```csharp
DateTime today = DateTime.Today;
DateTime futureDate = today.AddWorkdays(5);
```
##### Finding the Next Monday
```csharp
DateTime today = DateTime.Today;
DateTime nextMonday = today.NextWeekday(DayOfWeek.Monday);
```
---
### String Extensions
The `StringExtensions` class provides a wide range of methods for string manipulation, validation, and conversion.
---
#### Features
1. **Pattern Matching**:
- Check if a string matches a pattern using SQL-like wildcards.
2. **Substring Extraction**:
- Extract substrings from the left, right, or middle of a string.
3. **Type Conversion**:
- Convert strings to various types, such as integers, booleans, and enums.
---
#### Example Usage
##### Pattern Matching
```csharp
bool matches = "example".Like("exa*e"); // True
```
##### Substring Extraction
```csharp
string result = "example".Left(3); // "exa"
```
---
### Object Extensions
The `ObjectExtensions` class provides methods for serializing objects to JSON strings and deserializing JSON strings back to objects.
---
#### Features
1. **JSON Serialization**:
- Convert objects to JSON strings.
2. **JSON Deserialization**:
- Convert JSON strings back to objects.
---
#### Example Usage
##### Serialization
```csharp
var person = new { Name = "John", Age = 30 };
string json = person.ToJson();
```
##### Deserialization
```csharp
var person = json.ToObject<Person>();
```
---
### DataTable Extensions
The `DataTableExtensions` class provides methods for working with `DataTable` objects, such as counting duplicate rows and retrieving distinct records.
---
#### Features
1. **Count Duplicates**:
- Count duplicate rows between two `DataTable` instances.
2. **Retrieve Distinct Records**:
- Get distinct rows based on specified columns.
---
#### Example Usage
##### Counting Duplicates
```csharp
int duplicateCount = table1.DuplicatesCount(table2);
```
##### Retrieving Distinct Records
```csharp
DataTable distinctTable = table.DistinctRecords(new[] { "Name", "Age" });
```
---
## Logging
The `Logging` namespace provides a custom file-based logging implementation that integrates with the `Microsoft.Extensions.Logging` framework.
---
#### Features
1. **File-Based Logging**:
- Log messages to a specified file.
2. **Log Levels**:
- Supports all standard log levels.
3. **Thread Safety**:
- Ensures thread-safe writes to the log file.
---
#### Example Usage
```csharp
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddFile("logs.txt"));
var logger = services.BuildServiceProvider().GetRequiredService<ILogger<FileLogger>>();
logger.LogInformation("Logging to file!");
```
---
## Networking
### Network Connection
The `NetworkConnection` class provides methods for managing connections to network shares on Windows.
---
#### Features
1. **Connect to Network Shares**:
- Establish connections to shared network resources.
2. **Error Handling**:
- Provides detailed error messages for connection failures.
---
#### Example Usage
```csharp
var credentials = new NetworkCredential("username", "password");
if (NetworkConnection.TryCreate(logger, "\\server\share", credentials, out var connection, out var error)) {
connection.Dispose();
}
```
---
### Ping Port
The `PingPort` class provides methods for checking the reachability of a host on specified TCP or UDP ports.
---
#### Features
1. **TCP Port Checking**:
- Check if a TCP port is reachable.
2. **UDP Port Checking**:
- Check if a UDP port is reachable.
---
#### Example Usage
##### Checking a TCP Port
```csharp
if (PingPort.TryHostPort("example.com", 80, out var error)) {
Console.WriteLine("Port is reachable.");
}
```
---
## Security
### AES-GCM Utility
The `AESGCMUtility` class provides methods for encrypting and decrypting data using AES-GCM.
---
#### Features
1. **Secure Encryption**:
- Encrypt data with AES-GCM.
2. **Data Integrity**:
- Ensure data integrity with authentication tags.
---
#### Example Usage
##### Encrypting Data
```csharp
var key = AESGCMUtility.GenerateKeyBase64();
AESGCMUtility.TryEncryptData(data, key, out var encryptedData, out var error);
```
---
### Base32 Encoder
The `Base32Encoder` class provides methods for encoding and decoding data in Base32 format.
---
#### Features
1. **Encoding**:
- Encode binary data to Base32.
2. **Decoding**:
- Decode Base32 strings to binary data.
---
#### Example Usage
##### Encoding Data
```csharp
Base32Encoder.TryEncode(data, out var encoded, out var error);
```
---
### Checksum Utility
The `ChecksumUtility` class provides methods for calculating and verifying CRC32 checksums.
---
#### Features
1. **Checksum Calculation**:
- Calculate CRC32 checksums for data.
2. **Checksum Verification**:
- Verify data integrity using CRC32 checksums.
---
#### Example Usage
##### Calculating a Checksum
```csharp
ChecksumUtility.TryCalculateCRC32Checksum(data, out var checksum, out var error);
```
---
### Password Hasher
The `PasswordHasher` class provides methods for securely hashing and validating passwords.
---
#### Features
1. **Salted Hashing**:
- Hash passwords with a unique salt.
2. **Validation**:
- Validate passwords against stored hashes.
---
#### Example Usage
##### Hashing a Password
```csharp
PasswordHasher.TryCreateSaltedHash("password", out var hash, out var error);
```
---
### JWT Generator
The `JwtGenerator` class provides methods for generating and validating JSON Web Tokens (JWTs).
---
#### Features
1. **Token Generation**:
- Generate JWTs with claims and metadata.
2. **Token Validation**:
- Validate JWTs against a secret.
---
#### Example Usage
##### Generating a Token
```csharp
JwtGenerator.TryGenerateToken(secret, issuer, audience, 60, "user", roles, out var token, out var error);
```
---
### TOTP Generator
The `TotpGenerator` class provides methods for generating and validating Time-Based One-Time Passwords (TOTP).
---
#### Features
1. **TOTP Generation**:
- Generate TOTPs based on shared secrets.
2. **TOTP Validation**:
- Validate TOTPs with time tolerance.
---
#### Example Usage
##### Generating a TOTP
```csharp
TotpGenerator.TryGenerate(secret, TotpGenerator.GetCurrentTimeStepNumber(), out var totp, out var error);
```
---
## Others
### Culture
The `Culture` class provides methods for dynamically setting the culture for the current thread.
---
#### Features
1. **Dynamic Culture Setting**:
- Change the culture for the current thread.
---
#### Example Usage
##### Setting the Culture
```csharp
Culture.TrySet("fr-FR", out var error);
```
---
### Environment Variables
The `EnvVar` class provides methods for managing environment variables.
---
#### Features
1. **Add to PATH**:
- Add directories to the `PATH` environment variable.
2. **Set and Unset Variables**:
- Manage environment variables at different scopes.
---
#### Example Usage
##### Adding to PATH
```csharp
EnvVar.TryAddToPath("/usr/local/bin", out var error);
```
---
### File System
The `FileSystem` class provides methods for working with files and directories.
---
#### Features
1. **Copy Files and Folders**:
- Copy files or directories to a target location.
2. **Delete Files and Folders**:
- Delete files or directories.
---
#### Example Usage
##### Copying Files
```csharp
FileSystem.TryCopyToFolder("source", "destination", true, out var error);
```
---
### Processes
The `Processes` class provides methods for managing system processes.
---
#### Features
1. **Start Processes**:
- Start new processes with optional arguments.
2. **Kill Processes**:
- Terminate processes by name.
---
#### Example Usage
##### Starting a Process
```csharp
Processes.TryStart("notepad.exe", "", 0, false, out var error);
```
---