EasyExtensions.EntityFrameworkCore
0.1.88
dotnet add package EasyExtensions.EntityFrameworkCore --version 0.1.88
NuGet\Install-Package EasyExtensions.EntityFrameworkCore -Version 0.1.88
<PackageReference Include="EasyExtensions.EntityFrameworkCore" Version="0.1.88" />
paket add EasyExtensions.EntityFrameworkCore --version 0.1.88
#r "nuget: EasyExtensions.EntityFrameworkCore, 0.1.88"
// Install EasyExtensions.EntityFrameworkCore as a Cake Addin #addin nuget:?package=EasyExtensions.EntityFrameworkCore&version=0.1.88 // Install EasyExtensions.EntityFrameworkCore as a Cake Tool #tool nuget:?package=EasyExtensions.EntityFrameworkCore&version=0.1.88
EasyExtensions
Ready-to-use .NET Standard library for convenient development.
Purposes
- Easy to use - just add a few lines of code to start working with the library.
- Flexible - use the library as a base for your project.
- Fast - add new features and commands in a few minutes.
- Modern - use the latest technologies and approaches.
- Secure - protect your data and users.
- Open Source - contribute to the project and make it better.
- Free - use the library for free.
- Cross-platform - run the library on any platform.
- Lightweight - use only necessary features.
- Documented - read the documentation and start using the library.
Getting Started
- Start by importing the library into your project
# Basic package with BCL extensions
dotnet add package EasyExtensions
# AspNetCore package with additional features
dotnet add package EasyExtensions.AspNetCore
dotnet add package EasyExtensions.AspNetCore.Authorization
dotnet add package EasyExtensions.AspNetCore.Sentry
# Drawing package with native SixLabors.ImageSharp support
# This package is .NET Standard 2.1 and not using System.Drawing, so it can be used in any project.
dotnet add package EasyExtensions.Drawing
# Entity Framework Core package with absractions and ORM features
dotnet add package EasyExtensions.EntityFrameworkCore
dotnet add package EasyExtensions.EntityFrameworkCore.Npgsql
# Quartz package - scheduler integration and convenient reflection-based job registration
dotnet add package EasyExtensions.Quartz
# WebDav package - WebDav client with NextCloud support
dotnet add package EasyExtensions.WebDav
# Windows package - Windows-specific features with WinAPI
dotnet add package EasyExtensions.Windows
Contents
Extensions
Byte Array Extensions
/// <summary>
/// Calculate SHA512 hash of byte array.
/// </summary>
/// <param name="bytes"> Data to calculate hash. </param>
/// <returns> SHA512 hash of byte array. </returns>
string SHA512(this byte[] bytes);
Claims Principal Extensions
/// <summary>
/// Get user id.
/// </summary>
/// <param name="user"> User instance. </param>
/// <returns> User id. </returns>
/// <exception cref="KeyNotFoundException"> Throws when claim not found. </exception>
int GetId(this ClaimsPrincipal? user);
/// <summary>
/// Try get user id.
/// </summary>
/// <param name="user"> User instance. </param>
/// <returns> User id, or 0 if not found. </returns>
int TryGetId(this ClaimsPrincipal? user);
/// <summary>
/// Get user roles.
/// </summary>
/// <param name="user"> User instance. </param>
/// <param name="rolePrefix"> Role prefix, for example: "user-group-" prefix returns group like "user-group-admins" </param>
/// <returns> User roles. </returns>
IEnumerable<string> GetRoles(this ClaimsPrincipal user, string rolePrefix = "");
DateTime Extensions
/// <summary>
/// Remove microseconds from <see cref="DateTime"/>.
/// </summary>
/// <returns> DateTime without microseconds. </returns>
DateTime DropMicroseconds(this DateTime value);
/// <summary>
/// Remove microseconds from <see cref="DateTime"/>.
/// </summary>
/// <returns> DateTime without microseconds. </returns>
DateTimeOffset DropMicroseconds(this DateTimeOffset value);
/// <summary>
/// Create new datetime with same values but <see cref="DateTimeKind.Utc"/>.
/// </summary>
/// <returns> New datetime. </returns>
DateTime ToUniversalTimeWithoutOffset(this DateTime value);
/// <summary>
/// Convert datetime value to nullable datetime type.
/// </summary>
/// <returns> Wrapped datetime value. </returns>
DateTime? ToNullable(this DateTime value);
Enumerable Extensions
/// <summary>
/// Shuffles the elements of the <see cref="IEnumerable{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the elements in the <see cref="IEnumerable{T}"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable{T}"/> to shuffle.</param>
/// <returns>A shuffled <see cref="IEnumerable{T}"/>.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="source"/> is null.</exception>
IEnumerable<T> Shuffle<T>(this IEnumerable<T> source);
/// <summary>
/// Gets a random element from the <see cref="IEnumerable{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the elements in the <see cref="IEnumerable{T}"/>.</typeparam>
/// <param name="enumerable">The <see cref="IEnumerable{T}"/> to get a random element from.</param>
/// <returns>A random element from the <see cref="IEnumerable{T}"/>.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="enumerable"/> is null.</exception>
T Random<T>(this IEnumerable<T> enumerable);
Exception Extensions
/// <summary>
/// Create string with error message from all inner exceptions if exists.
/// </summary>
/// <returns> Error message. </returns>
string ToStringWithInner(this Exception exception);
HttpRequest Extensions
/// <summary>
/// Get remote host IP address using proxy "X-Real-IP", "CF-Connecting-IP", "X-Forwarded-For" headers, or connection remote IP address.
/// </summary>
/// <returns> IP address, or "Unknown" by default. </returns>
string GetRemoteAddress(this HttpRequest request);
Math Extensions
/// <summary>
/// Pow specified foundation to exponent.
/// </summary>
/// <param name="number"> Foundation. </param>
/// <param name="exponent"> Exponent of pow. </param>
/// <returns> Calculation result. </returns>
/// <exception cref="OverflowException"> Throws when calculation result is too big. </exception>
int Pow(this int number, int exponent);
Object Extensions
/// <summary>
/// Clone object with MemberwiseClone.
/// </summary>
/// <typeparam name="TObj"> Type of object. </typeparam>
/// <param name="obj"> Object to clone. </param>
/// <returns> Cloned object. </returns>
TObj MemberwiseClone<TObj>(this TObj obj);
ServiceCollection Extensions
/// <summary>
/// Adds <see cref="CpuUsageService"/> to the <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services"> Current <see cref="IServiceCollection"/> instance. </param>
/// <returns> Current <see cref="IServiceCollection"/> instance. </returns>
IServiceCollection AddCpuUsageService(this IServiceCollection services);
/// <summary>
/// Add all types inherited from TInterface.
/// </summary>
/// <param name="services"> Current <see cref="IServiceCollection"/> instance. </param>
/// <param name="serviceLifetime"> Service lifetime, default is Scoped. </param>
/// <typeparam name="TInterface"> Interface type. </typeparam>
/// <returns> Current <see cref="IServiceCollection"/> instance. </returns>
IServiceCollection AddTypesOfInterface<TInterface>(this IServiceCollection services, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped) where TInterface : class
Stream Extensions
/// <summary>
/// Reads the bytes from the current stream and writes them to the byte array.
/// </summary>
/// <returns> Received byte array. </returns>
/// <exception cref="IOException"> An I/O error occurred. </exception>
/// <exception cref="ArgumentNullException"> Destination is null. </exception>
/// <exception cref="ObjectDisposedException"> Either the current stream or the destination stream is disposed. </exception>
/// <exception cref="NotSupportedException"> The current stream does not support reading, or the destination stream does not support writing. </exception>
byte[] ReadToEnd(this Stream stream);
/// <summary>
/// Asynchronously reads the bytes from the current stream and writes them to the byte array.
/// </summary>
/// <returns> Received byte array. </returns>
/// <exception cref="ArgumentNullException"> Destination is null. </exception>
/// <exception cref="ObjectDisposedException"> Either the current stream or the destination stream is disposed. </exception>
/// <exception cref="NotSupportedException"> The current stream does not support reading, or the destination stream does not support writing. </exception>
public static async Task<byte[]> ReadToEndAsync(this Stream stream)
/// <summary>
/// Calculate SHA512 hash of byte stream.
/// </summary>
/// <param name="stream"> Data to calculate hash. </param>
/// <returns> SHA512 hash of byte stream. </returns>
string SHA512(this Stream stream);
String Extensions
/// <summary>
/// Make first letter as lower case. If text is null or whitespace - returns <see cref="string.Empty"/>
/// </summary>
/// <returns> Text with lower case first letter. </returns>
string ToLowerFirstLetter(this string text);
/// <summary>
/// Make first letter as upper case. If text is null or whitespace - returns <see cref="string.Empty"/>
/// </summary>
/// <returns> Text with upper case first letter. </returns>
string ToUpperFirstLetter(this string text);
/// <summary>
/// Create SHA512 hash of specified text string.
/// </summary>
/// <returns> SHA512 hash. </returns>
string SHA512(this string str);
/// <summary>
/// Read only numbers from specified string.
/// </summary>
/// <returns> Parsed number, or -1 by default. </returns>
long ReadOnlyNumbers(this string str);
IP Address Extensions
/// <summary>
/// Get network address.
/// </summary>
/// <param name="address"> IP address. </param>
/// <param name="subnetMask"> Subnet mask. </param>
/// <returns> Network address. </returns>
IPAddress GetNetwork(this IPAddress address, IPAddress subnetMask);
/// <summary>
/// Get broadcast address.
/// </summary>
/// <param name="address"> IP address. </param>
/// <param name="subnetMask"> Subnet mask. </param>
/// <returns> Broadcast address. </returns>
IPAddress GetBroadcast(this IPAddress address, IPAddress subnetMask);
/// <summary>
/// Get network address.
/// </summary>
/// <param name="address"> IP address. </param>
/// <param name="subnetMask"> Subnet mask. </param>
/// <returns> Network address. </returns>
/// <exception cref="ArgumentOutOfRangeException"> Thrown when subnet mask is invalid. </exception>
IPAddress GetNetwork(this IPAddress address, int subnetMask);
/// <summary>
/// Get broadcast address.
/// </summary>
/// <param name="address"> IP address. </param>
/// <param name="subnetMask"> Subnet mask. </param>
/// <returns> Broadcast address. </returns>
/// <exception cref="ArgumentOutOfRangeException"> Thrown when subnet mask is invalid. </exception>
IPAddress GetBroadcast(this IPAddress address, int subnetMask);
Helpers
DateTime Helpers
/// <summary>
/// Parse DateTime from JSON format ISO 8601.
/// </summary>
/// <returns> Parsed datetime with UTC kind. </returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="FormatException"></exception>
/// <exception cref="NotSupportedException"></exception>
DateTime ParseDateTime(string datetime);
/// <summary>
/// Parse DateTimeOffset from JSON format ISO 8601.
/// </summary>
/// <returns> Parsed datetime offset. </returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="FormatException"></exception>
DateTimeOffset ParseDateTimeOffset(string datetime);
Reflection Helpers
/// <summary>
/// Get all types inherited from interface.
/// </summary>
/// <typeparam name="TInterface"> Interface type. </typeparam>
/// <returns> All types inherited from interface. </returns>
IEnumerable<Type> GetTypesOfInterface<TInterface>() where TInterface : class
/// <summary>
/// Copy matching properties from source to destination.
/// </summary>
/// <param name="source"> Source object. </param>
/// <param name="destination"> Destination object. </param>
void CopyMatchingProperties(object source, object destination);
String Helpers
/// <summary>
/// Fast generate pseudo random string with <see cref="DefaultCharset"/> and string length.
/// </summary>
/// <returns> Pseudo-random string. </returns>
string CreatePseudoRandomString();
/// <summary>
/// Fast generate pseudo random string with <see cref="DefaultCharset"/> and specified string length.
/// </summary>
/// <param name="length"> Result string length. </param>
/// <returns> Pseudo-random string. </returns>
string CreatePseudoRandomString(int length);
/// <summary>
/// Fast generate pseudo random string with specified charset and string length.
/// </summary>
/// <param name="charset"> Generator charset. </param>
/// <param name="length"> Result string length. </param>
/// <returns> Pseudo-random string. </returns>
string CreatePseudoRandomString(int length, string charset);
/// <summary>
/// Generate random string with <see cref="DefaultCharset"/> and string length.
/// </summary>
/// <returns> Really random string. </returns>
string CreateRandomString();
/// <summary>
/// Generate random string with <see cref="DefaultCharset"/> and specified string length.
/// </summary>
/// <param name="length"> Result string length. </param>
/// <returns> Really random string. </returns>
string CreateRandomString(int length);
/// <summary>
/// Generate random string with specified charset and string length.
/// </summary>
/// <param name="charset"> Generator charset. </param>
/// <param name="length"> Result string length. </param>
/// <returns> Really random string. </returns>
string CreateRandomString(int length, string charset);
/// <summary>
/// Remove spaces from string - trim, replace new lines and multiple spaces.
/// </summary>
/// <param name="comment"></param>
/// <returns></returns>
string RemoveSpaces(string? comment);
IP Address Helpers
/// <summary>
/// Convert IP address to number.
/// </summary>
/// <param name="ipAddress"> IP address. </param>
/// <returns> IP address as number. </returns>
BigInteger IpToNumber(string ipAddress);
/// <summary>
/// Convert number to IP address.
/// </summary>
/// <param name="ipNumber"> IP address as number. </param>
/// <returns> IP address. </returns>
IPAddress NumberToIp(BigInteger ipNumber);
/// <summary>
/// Get subnet mask address.
/// </summary>
/// <param name="subnetMask"> Subnet mask. </param>
/// <returns> Subnet address. </returns>
/// <exception cref="ArgumentOutOfRangeException"> Thrown when subnet mask is invalid. </exception>
IPAddress GetMaskAddress(int subnetMask);
/// <summary>
/// Extract subnet mask from IP address.
/// </summary>
/// <param name="ip"> IP address. </param>
/// <returns> Subnet mask, or null if not found. </returns>
int? ExtractMask(string ip);
Quartz Extensions
Usually, Quartz integration requires a lot of boilerplate code. This library simplifies the process of adding Quartz jobs to the project.
ServiceCollection Extensions
/// <summary>
/// Adds Quartz jobs with <see cref="JobTriggerAttribute"/> to the <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">IServiceCollection instance.</param>
/// <returns> Current <see cref="IServiceCollection"/> instance. </returns>
IServiceCollection AddQuartzJobs(this IServiceCollection services);
example of usage:
builder.Services.AddQuartzJobs();
Attributes
JobTriggerAttribute
Example of usage:
[JobTrigger(minutes: 1)]
[DisallowConcurrentExecution]
public class TestJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.WriteLine("Test job executed.");
return Task.CompletedTask;
}
}
Authorization Extensions
ServiceCollection Extensions
/// <summary>
/// Adds CORS policy with origins.
/// </summary>
/// <param name="services"> <see cref="IServiceCollection"/> instance. </param>
/// <param name="policyName"> Name of the policy. </param>
/// <param name="origins"> Origins to add to the policy. </param>
/// <returns></returns>
IServiceCollection AddCorsWithOrigins(this IServiceCollection services, string policyName, params string[] origins);
/// <summary>
/// Adds JWT authentication from JwtSettings section or Jwt[Key] configuration values.
/// </summary>
/// <param name="services"> <see cref="IServiceCollection"/> instance. </param>
/// <param name="configuration"> Configuration from which to get JWT settings. </param>
/// <returns> Current <see cref="IServiceCollection"/> instance. </returns>
/// <exception cref="KeyNotFoundException"> When JwtSettings section is not set. </exception>
IServiceCollection AddJwt(this IServiceCollection services, IConfiguration configuration);
/// <summary>
/// Ignore <see cref="AuthorizeAttribute"/> when application is on development environment.
/// </summary>
/// <returns> Current <see cref="IServiceCollection"/> instance. </returns>
IServiceCollection AllowAnonymousOnDevelopment(this IServiceCollection services);
Drawing Extensions
Image Extensions
/// <summary>
/// Fit image to target size and copy and blur it to background.
/// </summary>
/// <param name="image">Target image.</param>
/// <param name="targetWidth">Target width.</param>
/// <param name="targetHeight">Target height.</param>
/// <param name="gaussianBlurLevel">Gaussian blur level (optional).</param>
/// <returns cref="Image">Image with blured background.</returns>
Image FitBluredBackground(this Image image, int targetWidth, int targetHeight, float gaussianBlurLevel = 8F);
/// <summary>
/// Save image as JPEG to byte array.
/// </summary>
/// <param name="image">Target image.</param>
/// <returns>Byte array of image.</returns>
byte[] SaveAsJpegToArray(this Image image);
Entity Framework Extensions
ServiceCollection Extensions
/// <summary>
/// Adds exception handler for EasyExtensions.EntityFrameworkCore.Exceptions to the <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services"> The <see cref="IServiceCollection"/> instance. </param>
/// <returns> Current <see cref="IServiceCollection"/> instance. </returns>
IServiceCollection AddExceptionHandler(this IServiceCollection services);
/// <summary>
/// Sets up Gridify.
/// </summary>
IServiceCollection AddGridifyMappers(this IServiceCollection services);
/// <summary>
/// Adds a <see cref="DbContext"/> to the <see cref="IServiceCollection"/> using the
/// <see cref="IConfigurationRoot"/> to build the connection string from DatabaseSettings section.
/// </summary>
/// <typeparam name="TContext"> The type of <see cref="DbContext"/> to add. </typeparam>
/// <param name="services"> The <see cref="IServiceCollection"/> instance. </param>
/// <param name="configuration"> The <see cref="IConfigurationRoot"/> instance. </param>
/// <param name="maxPoolSize"> The maximum pool size, default is 100. </param>
/// <param name="timeout_s"> The connection timeout in seconds, default is 60. </param>
/// <returns> Current <see cref="IServiceCollection"/> instance. </returns>
/// <exception cref="KeyNotFoundException"> When DatabaseSettings section is not set. </exception>
IServiceCollection AddDbContext<TContext>(this IServiceCollection services, IConfigurationRoot configuration, int maxPoolSize = 100, int timeout_s = 60) where TContext : DbContext
Host Extensions
/// <summary>
/// Applies migrations to the database.
/// </summary>
/// <returns> Current <see cref="IHost"/> instance. </returns>
IHost ApplyMigrations<TContext>(this IHost host) where TContext : DbContext
Sentry Extensions
WebHostBuilder Extensions
/// <summary>
/// Use Sentry integration with specified DSN.
/// </summary>
/// <param name="builder"> Current <see cref="IWebHostBuilder"/> instance. </param>
/// <param name="dsn"> Sentry DSN. </param>
/// <param name="forceUseInDevelopment"> Force use in development environment. </param>
/// <returns> Current <see cref="IWebHostBuilder"/> instance. </returns>
IWebHostBuilder UseSentryWithUserCapturing(this IWebHostBuilder builder, string dsn, bool forceUseInDevelopment = false);
Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
License
Distributed under the MIT License. See LICENSE.md for more information.
Contact
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. |
-
net9.0
- EasyExtensions (>= 0.1.88)
- Gridify (>= 2.15.1)
- Gridify.EntityFramework (>= 2.15.1)
- Microsoft.EntityFrameworkCore (>= 9.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.1.88 | 148 | 12/21/2024 |
0.1.87 | 194 | 12/17/2024 |
0.1.86 | 311 | 12/8/2024 |
0.1.85 | 80 | 12/8/2024 |
0.1.84 | 505 | 11/19/2024 |
0.1.83 | 240 | 11/15/2024 |
0.1.82 | 92 | 11/15/2024 |
0.1.81 | 163 | 11/13/2024 |
0.1.80 | 280 | 11/10/2024 |
0.1.79 | 462 | 10/22/2024 |
0.1.78 | 116 | 10/21/2024 |
0.1.77 | 124 | 10/21/2024 |
0.1.76 | 87 | 10/21/2024 |
0.1.75 | 94 | 10/21/2024 |
0.1.74 | 76 | 10/21/2024 |
0.1.73 | 320 | 10/17/2024 |
0.1.72 | 182 | 10/16/2024 |
0.1.71 | 85 | 10/15/2024 |
0.1.70 | 163 | 10/15/2024 |
0.1.69 | 90 | 10/15/2024 |
0.1.68 | 178 | 10/15/2024 |
0.1.67 | 89 | 10/15/2024 |
0.1.66 | 148 | 10/14/2024 |
0.1.65 | 106 | 10/13/2024 |
0.1.64 | 83 | 10/13/2024 |
0.1.63 | 99 | 10/12/2024 |
0.1.62 | 104 | 10/12/2024 |
0.1.61 | 87 | 10/12/2024 |
0.1.60 | 144 | 10/12/2024 |
0.1.59 | 84 | 10/12/2024 |
0.1.58 | 119 | 10/11/2024 |
0.1.57 | 389 | 9/30/2024 |
0.1.56 | 84 | 9/30/2024 |
0.1.55 | 97 | 9/27/2024 |
0.1.54 | 89 | 9/27/2024 |
0.1.53 | 103 | 9/27/2024 |
0.1.52 | 126 | 9/24/2024 |
0.1.51 | 82 | 9/24/2024 |
0.1.50 | 127 | 9/24/2024 |
0.1.49 | 245 | 9/18/2024 |
0.1.48 | 118 | 9/17/2024 |
0.1.47 | 144 | 9/15/2024 |
0.1.46 | 141 | 8/22/2024 |
0.1.45 | 298 | 8/9/2024 |
0.1.44 | 118 | 8/7/2024 |
0.1.43 | 98 | 8/7/2024 |
0.1.42 | 84 | 8/7/2024 |
0.1.41 | 285 | 8/5/2024 |
0.1.40 | 117 | 8/5/2024 |
0.1.39 | 67 | 8/5/2024 |
0.1.38 | 65 | 8/5/2024 |
0.1.37 | 118 | 8/4/2024 |
0.1.36 | 81 | 8/3/2024 |
0.1.35 | 93 | 8/2/2024 |
0.1.34 | 95 | 8/2/2024 |
0.1.33 | 79 | 8/1/2024 |
0.1.32 | 84 | 8/1/2024 |
0.1.31 | 73 | 8/1/2024 |
0.1.30 | 72 | 8/1/2024 |
0.1.29 | 76 | 8/1/2024 |
0.1.28 | 93 | 8/1/2024 |
0.1.27 | 97 | 7/30/2024 |
0.1.26 | 82 | 7/30/2024 |
0.1.25 | 106 | 7/28/2024 |
0.1.24 | 84 | 7/27/2024 |
0.1.23 | 111 | 7/27/2024 |
0.1.22 | 142 | 7/27/2024 |
0.1.21 | 85 | 7/27/2024 |
0.1.20 | 79 | 7/27/2024 |
0.1.19 | 97 | 7/27/2024 |
0.1.18 | 143 | 7/26/2024 |
0.1.17 | 83 | 7/26/2024 |
0.1.16 | 103 | 7/26/2024 |
0.1.15 | 92 | 7/26/2024 |
0.1.14 | 83 | 7/26/2024 |
0.1.13 | 90 | 7/26/2024 |
0.1.12 | 87 | 7/26/2024 |