MaksIT.Core
1.5.1
dotnet add package MaksIT.Core --version 1.5.1
NuGet\Install-Package MaksIT.Core -Version 1.5.1
<PackageReference Include="MaksIT.Core" Version="1.5.1" />
<PackageVersion Include="MaksIT.Core" Version="1.5.1" />
<PackageReference Include="MaksIT.Core" />
paket add MaksIT.Core --version 1.5.1
#r "nuget: MaksIT.Core, 1.5.1"
#:package MaksIT.Core@1.5.1
#addin nuget:?package=MaksIT.Core&version=1.5.1
#tool nuget:?package=MaksIT.Core&version=1.5.1
MaksIT.Core Library Documentation
Table of Contents
- Abstractions
- Extensions
- Logging
- Threading
- Networking
- Security
- Web API Models
- Sagas
- CombGuidGenerator
- Others
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
DomainObjectBaseto include an identifier. - Provides a common structure for domain entities that need unique IDs.
Example Usage
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
DtoObjectBaseto include an identifier. - Provides a common structure for DTOs that need unique IDs.
Example Usage
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
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
public class UserResponse : ResponseModelBase {
public required Guid Id { get; set; }
public required string Name { get; set; }
}
Features and Benefits
Consistency:
- Ensures a uniform structure for domain, DTO, and Web API models.
Extensibility:
- Base classes can be extended to include shared properties or methods as needed.
Type Safety:
- Generic identifiers (
T) ensure type safety for domain documents and DTOs.
- Generic identifiers (
Reusability:
- Common logic or properties can be added to base classes and reused across the application.
Example End-to-End Usage
// 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
Keep Base Classes Lightweight:
- Avoid adding unnecessary properties or methods to base classes.
Encapsulation:
- Use base classes to enforce encapsulation and shared behavior across entities.
Validation:
- Extend
RequestModelBaseorResponseModelBaseto include validation logic if needed.
- Extend
This structure promotes clean code principles, reducing redundancy and improving maintainability across the application layers.
CombGuidGenerator
The CombGuidGenerator class in the MaksIT.Core.Comb namespace provides methods for generating and extracting COMB GUIDs (GUIDs with embedded timestamps). COMB GUIDs improve index locality by combining randomness with a sortable timestamp.
Features
Generate COMB GUIDs:
- Create GUIDs with embedded timestamps for improved database indexing.
Extract Timestamps:
- Retrieve the embedded timestamp from a COMB GUID.
Support for Multiple Formats:
- Generate COMB GUIDs compatible with SQL Server and PostgreSQL.
Example Usage
Generating a COMB GUID
var baseGuid = Guid.NewGuid();
var timestamp = DateTime.UtcNow;
// Generate a COMB GUID for SQL Server
var combGuid = CombGuidGenerator.CreateCombGuid(baseGuid, timestamp, CombGuidType.SqlServer);
// Generate a COMB GUID for PostgreSQL
var combGuidPostgres = CombGuidGenerator.CreateCombGuid(baseGuid, timestamp, CombGuidType.PostgreSql);
Extracting a Timestamp
var extractedTimestamp = CombGuidGenerator.ExtractTimestamp(combGuid, CombGuidType.SqlServer);
Console.WriteLine($"Extracted Timestamp: {extractedTimestamp}");
Generating a COMB GUID with Current Timestamp
var combGuidWithCurrentTimestamp = CombGuidGenerator.CreateCombGuid(Guid.NewGuid(), CombGuidType.SqlServer);
Best Practices
Use COMB GUIDs for Indexing:
- COMB GUIDs are ideal for database indexing as they improve index locality.
Choose the Correct Format:
- Use
CombGuidType.SqlServerfor SQL Server andCombGuidType.PostgreSqlfor PostgreSQL.
- Use
Ensure UTC Timestamps:
- Always use UTC timestamps to ensure consistency across systems.
The CombGuidGenerator class simplifies the creation and management of COMB GUIDs, making it easier to work with GUIDs in database applications.
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
Strongly-Typed Enumerations:
- Combines the clarity of enums with the extensibility of classes.
- Supports additional fields, methods, or logic as needed.
Reflection Support:
- Dynamically retrieve all enumeration values with
GetAll.
- Dynamically retrieve all enumeration values with
Parsing Capabilities:
- Retrieve enumeration values by ID or display name.
Comparison and Equality:
- Fully implements equality and comparison operators for use in collections and sorting.
Example Usage
Defining an Enumeration
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
var allValues = Enumeration.GetAll<MyEnumeration>();
allValues.ToList().ForEach(Console.WriteLine);
Parsing by ID or Name
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
var difference = Enumeration.AbsoluteDifference(MyEnumeration.Value1, MyEnumeration.Value2);
Console.WriteLine($"Absolute Difference: {difference}"); // Output: 1
Using in Collections
var values = new List<MyEnumeration> { MyEnumeration.Value2, MyEnumeration.Value1 };
values.Sort(); // Orders by ID
Best Practices
Extend for Specific Enums:
- Create specific subclasses for each enumeration type.
Avoid Duplicates:
- Ensure unique IDs and names for each enumeration value.
Use Reflection Sparingly:
- Avoid calling
GetAllin performance-critical paths.
- Avoid calling
The Enumeration class provides a powerful alternative to traditional enums, offering flexibility and functionality for scenarios requiring additional metadata or logic.
Sagas
The Sagas namespace in the MaksIT.Core project provides a framework for managing distributed transactions or workflows. It includes classes for defining saga steps, contexts, and builders.
Features
Saga Context:
- Manage the state and data of a saga.
Saga Steps:
- Define individual steps in a saga workflow.
Saga Builder:
- Build and execute sagas dynamically.
Example Usage
Defining a Saga Step
public class MySagaStep : LocalSagaStep {
public override Task ExecuteAsync(LocalSagaContext context) {
// Perform step logic here
return Task.CompletedTask;
}
}
Building a Saga
var saga = new LocalSagaBuilder()
.AddStep(new MySagaStep())
.Build();
await saga.ExecuteAsync();
Best Practices
Idempotency:
- Ensure saga steps are idempotent to handle retries gracefully.
Error Handling:
- Implement robust error handling and compensation logic for failed steps.
State Management:
- Use the saga context to manage state and pass data between steps.
The Sagas namespace simplifies the implementation of distributed workflows, making it easier to manage complex transactions and processes.
Extensions
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
Combine Expressions:
- Combine two expressions using logical operators like
AndAlsoandOrElse.
- Combine two expressions using logical operators like
Negate Expressions:
- Negate an expression using the
Notmethod.
- Negate an expression using the
Batch Processing:
- Divide a collection into smaller batches for processing.
Example Usage
Combining Expressions
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
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
Add Workdays:
- Add a specified number of workdays to a date, excluding weekends and holidays.
Find Specific Dates:
- Find the next occurrence of a specific day of the week.
Month and Year Boundaries:
- Get the start or end of the current month or year.
Example Usage
Adding Workdays
DateTime today = DateTime.Today;
DateTime futureDate = today.AddWorkdays(5);
Finding the Next Monday
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
Pattern Matching:
- Check if a string matches a pattern using SQL-like wildcards.
Substring Extraction:
- Extract substrings from the left, right, or middle of a string.
Type Conversion:
- Convert strings to various types, such as integers, booleans, and enums.
Example Usage
Pattern Matching
bool matches = "example".Like("exa*e"); // True
Substring Extraction
string result = "example".Left(3); // "exa"
Object Extensions
The ObjectExtensions class provides advanced methods for working with objects, including serialization, deep cloning, and structural equality comparison.
Features
JSON Serialization:
- Convert objects to JSON strings with optional custom converters.
Deep Cloning:
- Create a deep clone of an object, preserving reference identity and supporting cycles.
Structural Equality:
- Compare two objects deeply for structural equality, including private fields.
Snapshot Reversion:
- Revert an object to a previous state by copying all fields from a snapshot.
Example Usage
JSON Serialization
var person = new { Name = "John", Age = 30 };
string json = person.ToJson();
// With custom converters
var converters = new List<JsonConverter> { new CustomConverter() };
string jsonWithConverters = person.ToJson(converters);
Deep Cloning
var original = new Person { Name = "John", Age = 30 };
var clone = original.DeepClone();
Structural Equality
var person1 = new Person { Name = "John", Age = 30 };
var person2 = new Person { Name = "John", Age = 30 };
bool areEqual = person1.DeepEqual(person2); // True
Snapshot Reversion
var snapshot = new Person { Name = "John", Age = 30 };
var current = new Person { Name = "Doe", Age = 25 };
current.RevertFrom(snapshot);
// current.Name is now "John"
// current.Age is now 30
Best Practices
Use Deep Cloning for Complex Objects:
- Ensure objects are deeply cloned when working with mutable reference types.
Validate Structural Equality:
- Use
DeepEqualfor scenarios requiring precise object comparisons.
- Use
Revert State Safely:
- Use
RevertFromto safely restore object states in tracked entities.
- Use
DataTable Extensions
The DataTableExtensions class provides methods for working with DataTable objects, such as counting duplicate rows and retrieving distinct records.
Features
Count Duplicates:
- Count duplicate rows between two
DataTableinstances.
- Count duplicate rows between two
Retrieve Distinct Records:
- Get distinct rows based on specified columns.
Example Usage
Counting Duplicates
int duplicateCount = table1.DuplicatesCount(table2);
Retrieving Distinct Records
DataTable distinctTable = table.DistinctRecords(new[] { "Name", "Age" });
Guid Extensions
The GuidExtensions class provides methods for working with Guid values, including converting them to nullable types.
Features
- Convert to Nullable:
- Convert a
Guidto a nullableGuid?, returningnullif theGuidis empty.
- Convert a
Example Usage
Converting to Nullable
Guid id = Guid.NewGuid();
Guid? nullableId = id.ToNullable();
Logging
The Logging namespace provides a custom file-based logging implementation that integrates with the Microsoft.Extensions.Logging framework.
File Logger
The FileLogger class in the MaksIT.Core.Logging namespace provides a simple and efficient way to log messages to plain text files. It supports log retention policies and ensures thread-safe writes using the LockManager.
Features
Plain Text Logging:
- Logs messages in a human-readable plain text format.
Log Retention:
- Automatically deletes old log files based on a configurable retention period.
Thread Safety:
- Ensures safe concurrent writes to the log file using the
LockManager.
- Ensures safe concurrent writes to the log file using the
Example Usage
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddFileLogger("logs", TimeSpan.FromDays(7)));
var logger = services.BuildServiceProvider().GetRequiredService<ILogger<FileLogger>>();
logger.LogInformation("Logging to file!");
JSON File Logger
The JsonFileLogger class in the MaksIT.Core.Logging namespace provides structured logging in JSON format. It is ideal for machine-readable logs and integrates seamlessly with log aggregation tools.
Features
JSON Logging:
- Logs messages in structured JSON format, including timestamps, log levels, and exceptions.
Log Retention:
- Automatically deletes old log files based on a configurable retention period.
Thread Safety:
- Ensures safe concurrent writes to the log file using the
LockManager.
- Ensures safe concurrent writes to the log file using the
Example Usage
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddJsonFileLogger("logs", TimeSpan.FromDays(7)));
var logger = services.BuildServiceProvider().GetRequiredService<ILogger<JsonFileLogger>>();
logger.LogInformation("Logging to JSON file!");
Threading
Lock Manager
The LockManager class in the MaksIT.Core.Threading namespace provides a robust solution for managing concurrency and rate limiting. It ensures safe access to shared resources in multi-threaded or multi-process environments.
Features
Thread Safety:
- Ensures mutual exclusion using a semaphore.
Rate Limiting:
- Limits the frequency of access to shared resources using a token bucket rate limiter.
Reentrant Locks:
- Supports reentrant locks for the same thread.
Example Usage
var lockManager = new LockManager();
await lockManager.ExecuteWithLockAsync(async () => {
// Critical section
Console.WriteLine("Executing safely");
});
lockManager.Dispose();
Networking
Network Connection
The NetworkConnection class provides methods for managing connections to network shares on Windows.
Features
Connect to Network Shares:
- Establish connections to shared network resources.
Error Handling:
- Provides detailed error messages for connection failures.
Example Usage
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
TCP Port Checking:
- Check if a TCP port is reachable.
UDP Port Checking:
- Check if a UDP port is reachable.
Example Usage
Checking a TCP Port
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
Secure Encryption:
- Encrypt data with AES-GCM.
Data Integrity:
- Ensure data integrity with authentication tags.
Example Usage
Encrypting Data
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
Encoding:
- Encode binary data to Base32.
Decoding:
- Decode Base32 strings to binary data.
Example Usage
Encoding Data
Base32Encoder.TryEncode(data, out var encoded, out var error);
Checksum Utility
The ChecksumUtility class provides methods for calculating and verifying CRC32 checksums.
Features
Checksum Calculation:
- Calculate CRC32 checksums for data.
Checksum Verification:
- Verify data integrity using CRC32 checksums.
Example Usage
Calculating a Checksum
ChecksumUtility.TryCalculateCRC32Checksum(data, out var checksum, out var error);
Password Hasher
The PasswordHasher class provides methods for securely hashing and validating passwords.
Features
Salted Hashing:
- Hash passwords with a unique salt.
Validation:
- Validate passwords against stored hashes.
Example Usage
Hashing a Password
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
Token Generation:
- Generate JWTs with claims and metadata.
Token Validation:
- Validate JWTs against a secret.
Example Usage
Generating a Token
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
TOTP Generation:
- Generate TOTPs based on shared secrets.
TOTP Validation:
- Validate TOTPs with time tolerance.
Example Usage
Generating a TOTP
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
- Dynamic Culture Setting:
- Change the culture for the current thread.
Example Usage
Setting the Culture
Culture.TrySet("fr-FR", out var error);
Environment Variables
The EnvVar class provides methods for managing environment variables.
Features
Add to PATH:
- Add directories to the
PATHenvironment variable.
- Add directories to the
Set and Unset Variables:
- Manage environment variables at different scopes.
Example Usage
Adding to PATH
EnvVar.TryAddToPath("/usr/local/bin", out var error);
File System
The FileSystem class provides methods for working with files and directories.
Features
Copy Files and Folders:
- Copy files or directories to a target location.
Delete Files and Folders:
- Delete files or directories.
Example Usage
Copying Files
FileSystem.TryCopyToFolder("source", "destination", true, out var error);
Processes
The Processes class provides methods for managing system processes.
Features
Start Processes:
- Start new processes with optional arguments.
Kill Processes:
- Terminate processes by name.
Example Usage
Starting a Process
Processes.TryStart("notepad.exe", "", 0, false, out var error);
Contact
For any inquiries or contributions, feel free to reach out:
- Email: maksym.sadovnychyy@gmail.com
- Author: Maksym Sadovnychyy (MAKS-IT)
| 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.AspNetCore.Cryptography.KeyDerivation (>= 9.0.10)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.3.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Logging (>= 9.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Logging.Console (>= 9.0.10)
- Microsoft.IdentityModel.Tokens (>= 8.14.0)
- System.IdentityModel.Tokens.Jwt (>= 8.14.0)
- System.Linq.Dynamic.Core (>= 1.6.9)
- System.Threading.RateLimiting (>= 9.0.10)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on MaksIT.Core:
| Package | Downloads |
|---|---|
|
MaksIT.MongoDB.Linq
MaksIT.MongoDB.Linq is a .NET library designed to facilitate working with MongoDB using LINQ queries, providing a seamless and intuitive interface for developers to interact with MongoDB databases. The library abstracts common data access patterns, allowing for more efficient and readable code when performing CRUD operations, managing sessions, and handling transactions. |
|
|
MaksIT.Dapr
MaksIT.Dapr is a facade library for Dapr. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.5.1 | 71 | 11/1/2025 |
| 1.5.0 | 56 | 11/1/2025 |
| 1.4.9 | 71 | 10/31/2025 |
| 1.4.8 | 139 | 10/30/2025 |
| 1.4.7 | 160 | 10/16/2025 |
| 1.4.6 | 159 | 10/16/2025 |
| 1.4.5 | 365 | 7/25/2025 |
| 1.4.4 | 379 | 7/25/2025 |
| 1.4.3 | 248 | 7/19/2025 |
| 1.4.2 | 170 | 7/6/2025 |
| 1.4.1 | 127 | 6/28/2025 |
| 1.4.0 | 263 | 5/25/2025 |
| 1.3.9 | 206 | 5/25/2025 |
| 1.3.8 | 282 | 5/15/2025 |
| 1.3.7 | 222 | 5/5/2025 |
| 1.3.6 | 123 | 1/12/2025 |
| 1.3.5 | 154 | 1/10/2025 |
| 1.3.4 | 182 | 1/5/2025 |
| 1.3.3 | 152 | 1/5/2025 |
| 1.3.2 | 152 | 1/3/2025 |
| 1.3.1 | 168 | 12/31/2024 |
| 1.3.0 | 156 | 12/30/2024 |
| 1.2.9 | 141 | 12/30/2024 |
| 1.2.8 | 191 | 12/15/2024 |
| 1.2.7 | 183 | 12/15/2024 |
| 1.2.6 | 157 | 12/14/2024 |
| 1.2.5 | 163 | 12/14/2024 |
| 1.2.4 | 180 | 12/14/2024 |
| 1.2.3 | 171 | 12/14/2024 |
| 1.2.2 | 175 | 12/7/2024 |
| 1.2.1 | 179 | 11/28/2024 |
| 1.2.0 | 122 | 11/27/2024 |
| 1.1.9 | 146 | 11/26/2024 |
| 1.1.8 | 152 | 11/23/2024 |
| 1.1.7 | 159 | 11/21/2024 |
| 1.1.6 | 176 | 10/27/2024 |
| 1.1.5 | 143 | 10/26/2024 |
| 1.1.4 | 193 | 10/18/2024 |
| 1.1.3 | 209 | 10/18/2024 |
| 1.1.2 | 165 | 10/17/2024 |
| 1.1.1 | 138 | 10/17/2024 |
| 1.1.0 | 171 | 10/13/2024 |
| 1.0.9 | 222 | 10/13/2024 |
| 1.0.8 | 177 | 10/5/2024 |
| 1.0.7 | 171 | 10/3/2024 |
| 1.0.6 | 165 | 10/3/2024 |
| 1.0.5 | 160 | 9/28/2024 |
| 1.0.4 | 176 | 9/26/2024 |
| 1.0.3 | 174 | 9/26/2024 |
| 1.0.2 | 202 | 9/15/2024 |
| 1.0.1 | 194 | 9/13/2024 |
| 1.0.0 | 179 | 9/3/2024 |