GameFrameX.Foundation.Options 2.4.3

dotnet add package GameFrameX.Foundation.Options --version 2.4.3
                    
NuGet\Install-Package GameFrameX.Foundation.Options -Version 2.4.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GameFrameX.Foundation.Options" Version="2.4.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GameFrameX.Foundation.Options" Version="2.4.3" />
                    
Directory.Packages.props
<PackageReference Include="GameFrameX.Foundation.Options" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GameFrameX.Foundation.Options --version 2.4.3
                    
#r "nuget: GameFrameX.Foundation.Options, 2.4.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GameFrameX.Foundation.Options@2.4.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GameFrameX.Foundation.Options&version=2.4.3
                    
Install as a Cake Addin
#tool nuget:?package=GameFrameX.Foundation.Options&version=2.4.3
                    
Install as a Cake Tool

<div align="center">

GameFrameX Logo

GameFrameX.Foundation

Version License .NET Documentation

All-in-One Solution for Indie Game Development · Empowering Indie Developers' Dreams

📖 Documentation🚀 Quick Start


🌐 Language: English | 简体中文 | 繁體中文 | 日本語 | 한국어


</div>

📦 Assembly Overview

Assembly Description NuGet Package Version Downloads
GameFrameX.Foundation.Encryption Encryption Library GameFrameX.Foundation.Encryption NuGet NuGet
GameFrameX.Foundation.Extensions Extension Methods GameFrameX.Foundation.Extensions NuGet NuGet
GameFrameX.Foundation.Hash Hash Library GameFrameX.Foundation.Hash NuGet NuGet
GameFrameX.Foundation.Http.Extension HttpClient Extensions GameFrameX.Foundation.Http.Extension NuGet NuGet
GameFrameX.Foundation.Http.Normalization HTTP Message Normalization GameFrameX.Foundation.Http.Normalization NuGet NuGet
GameFrameX.Foundation.Json JSON Serializer GameFrameX.Foundation.Json NuGet NuGet
GameFrameX.Foundation.Localization Localization Framework GameFrameX.Foundation.Localization NuGet NuGet
GameFrameX.Foundation.Logger Serilog Logger Config GameFrameX.Foundation.Logger NuGet NuGet
GameFrameX.Foundation.Options CLI Argument Parser GameFrameX.Foundation.Options NuGet NuGet
GameFrameX.Foundation.Orm.Attribute ORM Attributes GameFrameX.Foundation.Orm.Attribute NuGet NuGet
GameFrameX.Foundation.Orm.Entity ORM Entity Base GameFrameX.Foundation.Orm.Entity NuGet NuGet
GameFrameX.Foundation.Utility Utility Classes GameFrameX.Foundation.Utility NuGet NuGet

GameFrameX's foundational utility library providing a suite of high-performance, easy-to-use components covering encryption, hashing, HTTP, JSON, logging and more.

🚀 Quick Start

Installation

Install the required packages via NuGet:

# Install encryption library
dotnet add package GameFrameX.Foundation.Encryption

# Install extension methods library
dotnet add package GameFrameX.Foundation.Extensions

# Install hash library
dotnet add package GameFrameX.Foundation.Hash

# Install JSON library
dotnet add package GameFrameX.Foundation.Json

# Install localization framework
dotnet add package GameFrameX.Foundation.Localization

# Install logging library
dotnet add package GameFrameX.Foundation.Logger

# Install CLI argument parser
dotnet add package GameFrameX.Foundation.Options

# Install HTTP extensions
dotnet add package GameFrameX.Foundation.Http.Extension

# Install HTTP message normalization
dotnet add package GameFrameX.Foundation.Http.Normalization

Basic Usage

using GameFrameX.Foundation.Encryption;
using GameFrameX.Foundation.Extensions;
using GameFrameX.Foundation.Hash;
using GameFrameX.Foundation.Json;
using GameFrameX.Foundation.Localization.Core;
using GameFrameX.Foundation.Logger;
using GameFrameX.Foundation.Options;

// AES encryption
string encrypted = AesHelper.Encrypt("Hello World", "your-key");
string decrypted = AesHelper.Decrypt(encrypted, "your-key");

// Using extension methods
var list = new List<int> { 1, 2, 3, 4, 5 };
var randomItem = list.RandomElement(); // Get random element
var isNullOrEmpty = myString.IsNullOrEmpty(); // String check

// String extensions
string base64 = "SGVsbG8gV29ybGQ=";
string urlSafe = base64.ToUrlSafeBase64(); // URL-safe Base64
string centered = "Hello".CenterAlignedText(20); // Center aligned

// Object validation
object obj = GetSomeObject();
obj.ThrowIfNull(nameof(obj)); // Null check
int value = 50;
value.CheckRange(1, 100); // Range check

// High performance byte operations
Span<byte> buffer = stackalloc byte[8];
int offset = 0;
buffer.WriteUIntValue(12345u, ref offset);
buffer.WriteFloatValue(3.14f, ref offset);

// Bidirectional dictionary
var biDict = new BidirectionalDictionary<string, int>();
biDict.TryAdd("one", 1);
if (biDict.TryGetKey(1, out string key)) { /* Reverse lookup */ }

// CLI options
var builder = new OptionsBuilder<AppConfig>(args);
var config = builder.Build();

// SHA-256 hash
string hash = Sha256Helper.ComputeHash("Hello World");

// JSON serialization
string json = JsonHelper.Serialize(myObject);
MyClass obj = JsonHelper.Deserialize<MyClass>(json);

// Get localized string
var successMessage = LocalizationService.GetString("Success");
var errorMessage = LocalizationService.GetString("Utility.Exceptions.TimestampOutOfRange");
var formattedMessage = LocalizationService.GetString("Encryption.InvalidKeySize", 128, 256);

// Logging
LogHandler.Create(LogOptions.Default);
LogHelper.Info("Application started");

📚 Detailed Documentation

🧩 Extension Methods (GameFrameX.Foundation.Extensions)

A rich set of extension methods enhancing .NET base types, improving development efficiency and code readability.

Core Components Overview
Component File Description
Collection Extensions CollectionExtensions.cs Convenient operations for various collection types
String Extensions StringExtensions.cs Enhanced string processing including URL-safe Base64, center alignment
Object Extensions ObjectExtensions.cs Object validation and numeric range checking
Type Extensions TypeExtensions.cs Type checking and reflection extension methods
Enumerable Extensions IEnumerableExtensions.cs LINQ enhancements and set operations
Dictionary Extensions IDictionaryExtensions.cs Dictionary enhancements with merge, conditional remove
List Extensions ListExtensions.cs List-specific extension methods
Byte Extensions ByteExtensions.cs Byte array operations including sub-array extraction
Span Extensions SpanExtensions.cs High-performance memory operations with big/little endian support
ReadOnlySpan Extensions ReadOnlySpanExtensions.cs High-performance read-only memory operations
SequenceReader Extensions SequenceReaderExtensions.cs Convenient sequence data reading methods
Bidirectional Dictionary BidirectionalDictionary.cs Bidirectional lookup dictionary
Lookup Table LookupX.cs Enhanced one-to-many lookup table
Concurrent Queue ConcurrentLimitedQueue.cs Thread-safe bounded queue
Nullable Dictionary NullableDictionary.cs<br/>NullableConcurrentDictionary.cs Dictionary supporting null keys
Disposable Dictionary DisposableDictionary.cs<br/>DisposableConcurrentDictionary.cs Auto-disposable dictionary
Constants ConstBaseTypeSize.cs Base type byte size constants
Null Object NullObject.cs Type-safe null object implementation
Custom Exception ArgumentAlreadyException.cs Argument already exists exception
Collection Extensions
using GameFrameX.Foundation.Extensions;

// Collection operations
var list = new List<int> { 1, 2, 3, 4, 5 };
var randomItem = list.RandomElement(); // Get random element
var isEmpty = list.IsNullOrEmpty(); // Check if empty

// Dictionary extensions
var dict = new Dictionary<string, int>();
dict.Merge("key", 10, (old, new) => old + new); // Merge values
var value = dict.GetOrAdd("key", k => 42); // Get or add
dict.RemoveIf((k, v) => v > 100); // Conditional removal

// HashSet extensions
var hashSet = new HashSet<int>();
hashSet.AddRange(new[] { 1, 2, 3, 4, 5 }); // Batch add
String Extensions
// String check
string text = "Hello World";
bool isEmpty = text.IsNullOrEmpty();
bool isEmptyOrWhitespace = text.IsNullOrEmptyOrWhiteSpace();
bool hasContent = text.IsNotNullOrEmptyOrWhiteSpace();

// String processing
string base64 = "SGVsbG8gV29ybGQ=";
string urlSafe = base64.ToUrlSafeBase64(); // Convert to URL-safe format
string restored = urlSafe.FromUrlSafeBase64(); // Restore standard format

// String operations
string centered = "Hello".CenterAlignedText(20); // Center aligned
string cleaned = "Hello World   ".RemoveWhiteSpace(); // Remove whitespace
string trimmed = "Hello!".RemoveSuffix('!'); // Remove suffix

// Character repetition
string repeated = 'A'.RepeatChar(5); // "AAAAA"
Object Validation & Range Checking
// Null check
object obj = GetSomeObject();
if (obj.IsNotNull())
{
    // Handle non-null object
}

// Parameter validation
obj.ThrowIfNull(nameof(obj)); // Throw when null

// Numeric range check
int value = 50;
value.CheckRange(1, 100); // Check range, throw when out of bounds
bool inRange = value.IsRange(1, 100); // Check if within range

// Support multiple numeric types
uint uintValue = 25;
uintValue.CheckRange(0, 50);

long longValue = 1000;
longValue.CheckRange(500, 2000);
Type Checking Extensions
// Generic interface check
Type listType = typeof(List<string>);
Type genericListType = typeof(List<>);
bool implementsGeneric = listType.HasImplementedRawGeneric(genericListType);

// Interface implementation check
Type stringType = typeof(string);
Type comparableType = typeof(IComparable);
bool implementsInterface = stringType.IsImplWithInterface(comparableType);
LINQ Enhancement Extensions
// Intersection operations
var list1 = new[] { 1, 2, 3, 4, 5 };
var list2 = new[] { 3, 4, 5, 6, 7 };
var intersection = list1.IntersectBy(list2, x => x); // Intersect by key

// Multi-set intersection
var collections = new[] { list1, list2, new[] { 4, 5, 6 } };
var allIntersection = collections.IntersectAll(); // Intersection of all collections

// Difference operations
var difference = list1.ExceptBy(list2, (x, y) => x == y);

// Batch add
var collection = new List<int>();
collection.AddRange(1, 2, 3, 4, 5); // Using params
collection.AddRange(new[] { 6, 7, 8 }); // Using array
Bidirectional Dictionary
// Create bidirectional dictionary
var biDict = new BidirectionalDictionary<string, int>();

// Add key-value pair
biDict.TryAdd("one", 1);
biDict.TryAdd("two", 2);

// Bidirectional lookup
if (biDict.TryGetValue("one", out int value))
{
    Console.WriteLine($"Key 'one' maps to {value}");
}

if (biDict.TryGetKey(1, out string key))
{
    Console.WriteLine($"Value 1 maps to '{key}'");
}

// Clear dictionary
biDict.Clear();
High Performance Extensions
// Span and ReadOnlySpan extensions
ReadOnlySpan<byte> span = stackalloc byte[] { 1, 2, 3, 4, 5 };
// High performance Span operation extensions

// Sequence reader extensions
// Convenient read methods for SequenceReader
Byte Operation Extensions
// Byte array extensions
byte[] data = { 1, 2, 3, 4, 5 };
byte[] subArray = data.SubArray(1, 3); // Get sub-array

// Span and ReadOnlySpan extensions - High performance byte operations
Span<byte> buffer = stackalloc byte[16];
int offset = 0;

// Write various data types (big-endian and little-endian)
buffer.WriteUIntValue(12345u, ref offset);
buffer.WriteFloatValue(3.14f, ref offset);
buffer.WriteUIntBigEndianValue(12345u, ref offset); // Big-endian write
buffer.WriteFloatBigEndianValue(3.14f, ref offset); // Big-endian write

// Read data type
offset = 0;
uint value = buffer.ReadUIntValue(ref offset);
float floatValue = buffer.ReadFloatValue(ref offset);
uint bigEndianValue = buffer.ReadUIntBigEndianValue(ref offset); // Big-endian read

// ReadOnlySpan read operations
ReadOnlySpan<byte> readBuffer = buffer;
offset = 0;
uint readValue = readBuffer.ReadUIntValue(ref offset);
float readFloatValue = readBuffer.ReadFloatBigEndianValue(ref offset);
Sequence Reader Extensions
// Convenient read methods for SequenceReader
// Support length-prefixed byte array reading
// Non-destructive read with TryPeek
Special Utility Classes
  • ConstBaseTypeSize: Base type byte size constants for all .NET primitive types
  • NullObject: Type-safe null object pattern implementation
  • NullableConcurrentDictionary: Thread-safe concurrent dictionary supporting null values
  • NullableDictionary: Dictionary supporting null values
  • LookupX: Enhanced lookup table with one-to-many mapping
  • ArgumentAlreadyException: Argument already exists exception for parameter validation
  • ConcurrentLimitedQueue: Thread-safe bounded queue that auto-removes oldest elements
  • DisposableConcurrentDictionary/DisposableDictionary: Auto-disposable dictionary types

🔐 Encryption Library (GameFrameX.Foundation.Encryption)

Provides implementations of multiple encryption algorithms for secure data transmission and storage.

Supported Algorithms
  • AES Encryption (AesHelper): Symmetric encryption supporting strings and byte arrays
  • RSA Encryption (RsaHelper): Asymmetric encryption with key generation, encryption/decryption and digital signatures
  • DSA Signing (DsaHelper): Digital signature algorithm supporting signing and verification
  • SM2/SM4 Encryption (Sm2Helper/Sm4Helper): National cryptographic algorithm implementation
    • SM2: Asymmetric encryption
    • SM4: Symmetric encryption, ECB/CBC modes
  • XOR Encryption (XorHelper): XOR encryption with fast and complete encryption modes
Usage Examples
// AES encryption
string encrypted = AesHelper.Encrypt("Sensitive data", "your-secret-key");
string decrypted = AesHelper.Decrypt(encrypted, "your-secret-key");

// RSA encryption
var keys = RsaHelper.Make();
string encrypted = RsaHelper.Encrypt(keys["publicKey"], "Hello World");
string decrypted = RsaHelper.Decrypt(keys["privateKey"], encrypted);

// SM4 encryption
string encrypted = Sm4Helper.EncryptCbc("your-key", "Hello World");
string decrypted = Sm4Helper.DecryptCbc("your-key", encrypted);

🔗 Hash Library (GameFrameX.Foundation.Hash)

Provides multiple hash algorithm implementations for data integrity verification, fast lookup and more.

Supported Algorithms
  • MD5 (Md5Helper): 128-bit hash with salt support
  • SHA Series:
    • SHA-1 (Sha1Helper): 160-bit hash
    • SHA-256 (Sha256Helper): 256-bit hash
    • SHA-512 (Sha512Helper): 512-bit hash
  • HMAC-SHA256 (HmacSha256Helper): Key-based message authentication code
  • CRC Checksum (CrcHelper): CRC32/CRC64 cyclic redundancy check
  • MurmurHash3 (MurmurHash3Helper): High-performance non-cryptographic hash
  • xxHash (XxHashHelper): Ultra-high performance hash, 32/64/128-bit
Usage Examples
// MD5 hash
string md5Hash = Md5Helper.Hash("Hello World");
string saltedHash = Md5Helper.HashWithSalt("Hello World", "salt");

// SHA-256 hash
string sha256Hash = Sha256Helper.ComputeHash("Hello World");

// HMAC-SHA256
string hmacHash = HmacSha256Helper.Hash("message", "secret-key");

// xxHash (High performance)
ulong xxHash = XxHashHelper.Hash64("Hello World");

🌐 HTTP Tools

HTTP Extensions (GameFrameX.Foundation.Http.Extension)

Convenient extension methods for HttpClient, simplifying JSON data sending and receiving.

// POST JSON request
string response = await httpClient.PostJsonToStringAsync<MyClass>(url, myObject);
HTTP Message Normalization (GameFrameX.Foundation.Http.Normalization)

Provides a unified HTTP response format with code, message and data fields for the GameFrameX ecosystem.

📄 JSON Serialization (GameFrameX.Foundation.Json)

High-performance serialization tool based on System.Text.Json with optimized defaults.

Features
  • High-performance serialization/deserialization
  • Enums serialized as strings
  • Ignore null value properties
  • Ignore circular references
  • Case-insensitive property names
  • Formatted and compact output modes
Usage Examples
// Serialization
string json = JsonHelper.Serialize(myObject);
string formattedJson = JsonHelper.Serialize(myObject, JsonHelper.FormatOptions);

// Deserialization
MyClass obj = JsonHelper.Deserialize<MyClass>(json);

// Safe deserialization
if (JsonHelper.TryDeserialize<MyClass>(json, out var result))
{
    // Process result
}

🌐 Localization Framework (GameFrameX.Foundation.Localization)

A lightweight, high-performance localization solution with zero-config usage and lazy loading, providing unified localization support for the entire GameFrameX.Foundation ecosystem.

Key Features
  • Zero-config usage: No initialization required, auto-discovers and loads localization resources
  • Lazy loading: Resources loaded on first use for excellent startup performance
  • Multi-language support: Built-in Chinese (Simplified) and English, extensible to more languages
  • Thread safe: Supports concurrent access for multi-threaded environments
  • Highly extensible: Custom resource providers with flexible priority management
  • Priority resolution: Custom providers > Assembly resources > Default resources
Core Components
Component File Description
Localization Service LocalizationService.cs Unified localization entry point with static method API
Resource Manager ResourceManager.cs Manages multiple resource providers with priority resolution
Default Provider DefaultResourceProvider.cs Provides English default messages with 50+ common messages
Assembly Provider AssemblyResourceProvider.cs Loads localization resources from .resx files
Basic Usage
using GameFrameX.Foundation.Localization.Core;

// Get simple localized string
var successMessage = LocalizationService.GetString("Success");
Console.WriteLine(successMessage); // Display based on current culture "Success" or "Success"

// Parameterized formatted message
var errorMessage = LocalizationService.GetString("ArgumentNull", "username");
Console.WriteLine(errorMessage); // "Value cannot be null. (Parameter 'username')"

// If key not found, return key name itself
var unknown = LocalizationService.GetString("Some.Unknown.Key");
Console.WriteLine(unknown); // Output: "Some.Unknown.Key"
Localization in Exception Handling
using GameFrameX.Foundation.Utility.Localization;

public class UserService
{
    public void ValidateUserInput(string input)
    {
        if (string.IsNullOrEmpty(input))
        {
            throw new ArgumentException(
                LocalizationService.GetString(LocalizationKeys.Exceptions.TimestampOutOfRange),
                nameof(input));
        }

        // Other validation logic...
    }
}
Module Localization Integration
1. Define Localization Keys
// YourModule/Localization/Keys.cs
namespace GameFrameX.Foundation.YourModule.Localization;

public static class LocalizationKeys
{
    public static class Validation
    {
        public const string EmailRequired = "YourModule.Validation.EmailRequired";
        public const string EmailInvalid = "YourModule.Validation.EmailInvalid";
    }

    public static class Messages
    {
        public const string UserCreated = "YourModule.Messages.UserCreated";
        public const string OperationFailed = "YourModule.Messages.OperationFailed";
    }
}
2. Create Resource Files

Create Localization/Messages/Resources.resx and Localization/Messages/Resources.zh-CN.resx in the project:


<root>
  <data name="YourModule.Validation.EmailRequired" xml:space="preserve">
    <value>Email address is required</value>
  </data>
  <data name="YourModule.Messages.UserCreated" xml:space="preserve">
    <value>User '{0}' has been created successfully</value>
  </data>
</root>

<root>
  <data name="YourModule.Validation.EmailRequired" xml:space="preserve">
    <value>Email address is required</value>
  </data>
  <data name="YourModule.Messages.UserCreated" xml:space="preserve">
    <value>User '{0}' has been created successfully</value>
  </data>
</root>
3. Use in Business Logic
using GameFrameX.Foundation.Localization.Core;
using GameFrameX.Foundation.YourModule.Localization;

public class UserService
{
    public void CreateUser(UserDto userDto)
    {
        if (string.IsNullOrEmpty(userDto.Email))
        {
            throw new ValidationException(
                LocalizationService.GetString(LocalizationKeys.Validation.EmailRequired));
        }

        // Create user logic...

        var successMessage = LocalizationService.GetString(
            LocalizationKeys.Messages.UserCreated, userDto.Username);
        Console.WriteLine(successMessage);
    }
}
Custom Resource Providers
public class DatabaseResourceProvider : IResourceProvider
{
    private readonly IDbConnection _connection;

    public DatabaseResourceProvider(IDbConnection connection)
    {
        _connection = connection;
    }

    public string GetString(string key)
    {
        var culture = CultureInfo.CurrentCulture.Name;
        var sql = "SELECT localized_text FROM localization_strings WHERE key = @key AND culture = @culture";
        return _connection.ExecuteScalar<string>(sql, new { key, culture });
    }
}

// Register custom provider (highest priority)
var dbProvider = new DatabaseResourceProvider(yourDbConnection);
LocalizationService.RegisterProvider(dbProvider);
Preloading & Performance Optimization
// Preload all localization resources at startup (optional)
LocalizationService.EnsureLoaded();

// Get localization system statistics
var stats = LocalizationService.GetStatistics();
Console.WriteLine($"Providers loaded: {stats.ProvidersLoaded}");
Console.WriteLine($"Total provider count: {stats.TotalProviderCount}");
Console.WriteLine($"Assembly provider count: {stats.AssemblyProviderCount}");

// Get all provider information
var providers = LocalizationService.GetProviders();
foreach (var provider in providers)
{
    Console.WriteLine($"Provider: {provider.GetType().Name}");
}
Resource Naming Convention
  • Pattern: {Module}.{Category}.{SpecificKeyName}
  • Example:
    • Utility.Exceptions.TimestampOutOfRange
    • Encryption.InvalidKeySize
    • Authentication.UserNotFound
    • Success
    • ArgumentNull
Integrated Modules

The following modules have completed localization integration:

Module Localization Keys Status
GameFrameX.Foundation.Utility 4 ✅ Done
GameFrameX.Foundation.Encryption 20+ ✅ Done
GameFrameX.Foundation.Extensions 7 ✅ Done
GameFrameX.Foundation.Hash 2 ✅ Done
Advanced Features
Dynamic Language Switching
public void SwitchLanguage(string cultureCode)
{
    Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureCode);
    Thread.CurrentThread.CurrentCulture = new CultureInfo(cultureCode);

    // Optional: preload resources for new language
    LocalizationService.EnsureLoaded();
}
Monitoring & Diagnostics
public class LocalizationDiagnostics
{
    public void PrintStatus()
    {
        var stats = LocalizationService.GetStatistics();
        Console.WriteLine("=== Localization System Status ===");
        Console.WriteLine($"Providers loaded: {stats.ProvidersLoaded}");
        Console.WriteLine($"Total provider count: {stats.TotalProviderCount}");

        var providers = LocalizationService.GetProviders();
        foreach (var provider in providers)
        {
            Console.WriteLine($"- {provider.GetType().Name}");
        }
    }
}
Best Practices
  1. Key naming convention: Use the {Module}.{Category}.{KeyName} naming pattern
  2. Parameterized messages: Use string.Format for parameter substitution
  3. Exception handling: integrate localization support in exception messages
  4. Performance optimization: optionally preload resources at app startup
  5. Test verification: write unit tests for localization functions
Configure Project File

Ensure the project file includes localization resources:

<PropertyGroup>
  <EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
</PropertyGroup>

<ItemGroup>
  <EmbeddedResource Include="Localization\Messages\*.resx" />
</ItemGroup>

For more details, see:

�️ ORM Entity Base (GameFrameX.Foundation.Orm.Entity)

Provides ORM entity base classes and interface definitions supporting audit trails, soft deletes, optimistic locking and other enterprise features.

Core Components Overview
Component File Description
Entity Base EntityBase.cs Full-featured entity base with ID, audit, soft delete, version control
Entity Base (Generic) EntityBaseId.cs Entity base with custom primary key type
Entity Interface IEntity.cs Base entity interface providing ID property
Audit Interface IAuditableEntity.cs Audit interface defining creation/update time and user fields
Entity Base Class Features
using GameFrameX.Foundation.Orm.Entity;

// Classes inheriting EntityBase automatically gain full enterprise features
public class User : EntityBase
{
    public string Username { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    
    // Properties provided by EntityBase:
    // - long Id                    // Primary key ID
    // - DateTime CreateTime        // Created time
    // - DateTime UpdateTime        // Updated time
    // - long CreateUserId          // Creator user ID
    // - long UpdateUserId          // Updater user ID
    // - string CreateUserName      // Creator user name
    // - string UpdateUserName      // Updater user name
    // - bool IsDelete              // Soft delete flag
    // - long Version               // Optimistic lock version
    // - bool IsEnabled             // Enabled status
}

// Usage Examples
var user = new User
{
    Username = "john_doe",
    Email = "john@example.com",
    PasswordHash = "hashed_password",
    CreateTime = DateTime.UtcNow,
    CreateUserId = 1,
    CreateUserName = "admin",
    IsEnabled = true
};
Custom Primary Key Types
using GameFrameX.Foundation.Orm.Entity;

// Use string as primary key
public class Product : EntityBaseId<string>
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Description { get; set; }
    
    // Id property type is string, provided by EntityBaseId<string>
}

// Use Guid as primary key
public class Order : EntityBaseId<Guid>
{
    public string OrderNumber { get; set; }
    public decimal TotalAmount { get; set; }
    public DateTime OrderDate { get; set; }
    
    // Id property type is Guid, provided by EntityBaseId<Guid>
}

// Usage Examples
var product = new Product
{
    Id = "PROD-001",
    Name = "Laptop",
    Price = 5999.99m,
    Description = "High performance laptop"
};

var order = new Order
{
    Id = Guid.NewGuid(),
    OrderNumber = "ORD-20240101-001",
    TotalAmount = 5999.99m,
    OrderDate = DateTime.UtcNow
};
Interface Implementation
using GameFrameX.Foundation.Orm.Entity;

// Implement base entity interface
public class Category : IEntity<int>
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

// Implement audit interface
public class AuditableCategory : IEntity<int>, IAuditableEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    
    // Properties required by IAuditableEntity interface
    public DateTime CreateTime { get; set; }
    public DateTime UpdateTime { get; set; }
    public long CreateUserId { get; set; }
    public long UpdateUserId { get; set; }
    public string CreateUserName { get; set; }
    public string UpdateUserName { get; set; }
}
Enterprise Features
1. Audit Trail
// EntityBase auto-provides audit fields
public class Document : EntityBase
{
    public string Title { get; set; }
    public string Content { get; set; }
}

// Set audit info in business logic
var document = new Document
{
    Title = "Important Document",
    Content = "Document content...",
    CreateTime = DateTime.UtcNow,
    CreateUserId = currentUser.Id,
    CreateUserName = currentUser.Username,
    UpdateTime = DateTime.UtcNow,
    UpdateUserId = currentUser.Id,
    UpdateUserName = currentUser.Username
};

// Auto-maintain audit info on update
document.Content = "Updated content";
document.UpdateTime = DateTime.UtcNow;
document.UpdateUserId = currentUser.Id;
document.UpdateUserName = currentUser.Username;
document.Version++; // Optimistic lock version increment
2. Soft Delete
// Soft delete: mark as deleted instead of physically deleting
public void SoftDeleteUser(User user)
{
    user.IsDelete = true;
    user.UpdateTime = DateTime.UtcNow;
    user.UpdateUserId = currentUser.Id;
    user.UpdateUserName = currentUser.Username;
    
    // Save to database, record exists but marked as deleted
    dbContext.SaveChanges();
}

// Filter deleted records in queries
var activeUsers = dbContext.Users
    .Where(u => !u.IsDelete)
    .ToList();

// Restore deleted record
public void RestoreUser(User user)
{
    user.IsDelete = false;
    user.UpdateTime = DateTime.UtcNow;
    user.UpdateUserId = currentUser.Id;
    user.UpdateUserName = currentUser.Username;
    
    dbContext.SaveChanges();
}
3. Optimistic Locking
// Implement optimistic locking with Version field
public void UpdateUserWithOptimisticLock(long userId, string newEmail)
{
    var user = dbContext.Users.Find(userId);
    if (user == null) throw new EntityNotFoundException();
    
    var originalVersion = user.Version;
    
    // Modify data
    user.Email = newEmail;
    user.UpdateTime = DateTime.UtcNow;
    user.UpdateUserId = currentUser.Id;
    user.UpdateUserName = currentUser.Username;
    user.Version++; // Version number increment
    
    try
    {
        // Check version on save
        var rowsAffected = dbContext.Database.ExecuteSqlRaw(
            "UPDATE Users SET Email = {0}, UpdateTime = {1}, UpdateUserId = {2}, UpdateUserName = {3}, Version = {4} " +
            "WHERE Id = {5} AND Version = {6}",
            user.Email, user.UpdateTime, user.UpdateUserId, user.UpdateUserName, user.Version, user.Id, originalVersion);
            
        if (rowsAffected == 0)
        {
            throw new ConcurrencyException("Data modified by another user, please refresh and retry");
        }
    }
    catch (DbUpdateConcurrencyException)
    {
        throw new ConcurrencyException("Concurrency conflict, please refresh and retry");
    }
}
4. Enable/Disable State Management
// Manage entity enabled status using IsEnabled field
public class Feature : EntityBase
{
    public string Name { get; set; }
    public string Description { get; set; }
    // IsEnabled provided by EntityBase
}

// Enable/disable functionality
public void ToggleFeature(long featureId, bool enabled)
{
    var feature = dbContext.Features.Find(featureId);
    if (feature == null) throw new EntityNotFoundException();
    
    feature.IsEnabled = enabled;
    feature.UpdateTime = DateTime.UtcNow;
    feature.UpdateUserId = currentUser.Id;
    feature.UpdateUserName = currentUser.Username;
    feature.Version++;
    
    dbContext.SaveChanges();
}

// Query enabled functions
var enabledFeatures = dbContext.Features
    .Where(f => f.IsEnabled && !f.IsDelete)
    .ToList();
Complete Usage Example
using GameFrameX.Foundation.Orm.Entity;
using Microsoft.EntityFrameworkCore;

namespace MyApplication.Entities
{
    // User entity
    public class User : EntityBase
    {
        public string Username { get; set; }
        public string Email { get; set; }
        public string PasswordHash { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime? LastLoginTime { get; set; }
        
        // Navigation property
        public virtual ICollection<Order> Orders { get; set; }
        public virtual ICollection<UserRole> UserRoles { get; set; }
    }
    
    // Order entity (using Guid primary key)
    public class Order : EntityBaseId<Guid>
    {
        public string OrderNumber { get; set; }
        public long UserId { get; set; }
        public decimal TotalAmount { get; set; }
        public DateTime OrderDate { get; set; }
        public OrderStatus Status { get; set; }
        
        // Navigation property
        public virtual User User { get; set; }
        public virtual ICollection<OrderItem> OrderItems { get; set; }
    }
    
    // Order item entity
    public class OrderItem : EntityBase
    {
        public Guid OrderId { get; set; }
        public string ProductId { get; set; }
        public int Quantity { get; set; }
        public decimal UnitPrice { get; set; }
        public decimal TotalPrice { get; set; }
        
        // Navigation property
        public virtual Order Order { get; set; }
        public virtual Product Product { get; set; }
    }
    
    // Product entity (using string primary key)
    public class Product : EntityBaseId<string>
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public int StockQuantity { get; set; }
        public string CategoryId { get; set; }
        
        // Navigation property
        public virtual Category Category { get; set; }
        public virtual ICollection<OrderItem> OrderItems { get; set; }
    }
    
    // Category entity (implementing interfaces)
    public class Category : IEntity<string>, IAuditableEntity
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string ParentId { get; set; }
        
        // IAuditableEntity interface properties
        public DateTime CreateTime { get; set; }
        public DateTime UpdateTime { get; set; }
        public long CreateUserId { get; set; }
        public long UpdateUserId { get; set; }
        public string CreateUserName { get; set; }
        public string UpdateUserName { get; set; }
        
        // Navigation property
        public virtual Category Parent { get; set; }
        public virtual ICollection<Category> Children { get; set; }
        public virtual ICollection<Product> Products { get; set; }
    }
    
    public enum OrderStatus
    {
        Pending = 0,
        Confirmed = 1,
        Shipped = 2,
        Delivered = 3,
        Cancelled = 4
    }
}

// Business service example
namespace MyApplication.Services
{
    public class UserService
    {
        private readonly ApplicationDbContext _context;
        private readonly ICurrentUserService _currentUserService;
        
        public UserService(ApplicationDbContext context, ICurrentUserService currentUserService)
        {
            _context = context;
            _currentUserService = currentUserService;
        }
        
        public async Task<User> CreateUserAsync(string username, string email, string password)
        {
            var currentUser = await _currentUserService.GetCurrentUserAsync();
            
            var user = new User
            {
                Username = username,
                Email = email,
                PasswordHash = HashPassword(password),
                CreateTime = DateTime.UtcNow,
                UpdateTime = DateTime.UtcNow,
                CreateUserId = currentUser.Id,
                UpdateUserId = currentUser.Id,
                CreateUserName = currentUser.Username,
                UpdateUserName = currentUser.Username,
                IsEnabled = true,
                IsDelete = false,
                Version = 1
            };
            
            _context.Users.Add(user);
            await _context.SaveChangesAsync();
            
            return user;
        }
        
        public async Task<User> UpdateUserAsync(long userId, string email, string firstName, string lastName)
        {
            var user = await _context.Users
                .Where(u => u.Id == userId && !u.IsDelete)
                .FirstOrDefaultAsync();
                
            if (user == null)
                throw new EntityNotFoundException($"User {userId} does not exist");
            
            var currentUser = await _currentUserService.GetCurrentUserAsync();
            var originalVersion = user.Version;
            
            // Update field
            user.Email = email;
            user.FirstName = firstName;
            user.LastName = lastName;
            user.UpdateTime = DateTime.UtcNow;
            user.UpdateUserId = currentUser.Id;
            user.UpdateUserName = currentUser.Username;
            user.Version++;
            
            try
            {
                await _context.SaveChangesAsync();
                return user;
            }
            catch (DbUpdateConcurrencyException)
            {
                throw new ConcurrencyException("User info modified by another user, please refresh and retry");
            }
        }
        
        public async Task SoftDeleteUserAsync(long userId)
        {
            var user = await _context.Users
                .Where(u => u.Id == userId && !u.IsDelete)
                .FirstOrDefaultAsync();
                
            if (user == null)
                throw new EntityNotFoundException($"User {userId} does not exist");
            
            var currentUser = await _currentUserService.GetCurrentUserAsync();
            
            user.IsDelete = true;
            user.UpdateTime = DateTime.UtcNow;
            user.UpdateUserId = currentUser.Id;
            user.UpdateUserName = currentUser.Username;
            user.Version++;
            
            await _context.SaveChangesAsync();
        }
        
        public async Task<List<User>> GetActiveUsersAsync()
        {
            return await _context.Users
                .Where(u => u.IsEnabled && !u.IsDelete)
                .OrderBy(u => u.CreateTime)
                .ToListAsync();
        }
        
        private string HashPassword(string password)
        {
            // Implement password hash logic
            return BCrypt.Net.BCrypt.HashPassword(password);
        }
    }
}

### 🏷️ ORM Attribute Markers (GameFrameX.Foundation.Orm.Attribute)

Provides ORM framework attribute markers to identify special entity class features such as audit trails, caching strategies, soft deletes, and version control.

#### Core Components Overview

| Component | File | Main Function |
|--------------|------------------------|-----------------------------------------|
| **Audit Table Attribute** | `AuditTableAttribute.cs` | Marks entity classes for audit trail functionality, recording data change history |
| **Cache Table Attribute**    | `CacheTableAttribute.cs` | Marks entities for caching strategy to improve data access performance                    |
| **Soft Delete Attribute** | `SoftDeleteAttribute.cs` | Marks entity classes for soft delete functionality (logical delete, not physical) |
| **Version Control Attribute**   | `VersionControlAttribute.cs` | Marks entities for data versioning with optimistic locking and concurrency control               |

#### Audit Table Attribute (AuditTableAttribute)

Marks entity classes that require audit trails. The system automatically records data creation, modification, and deletion history.

```csharp
using GameFrameX.Foundation.Orm.Attribute;
using GameFrameX.Foundation.Orm.Entity;

// Mark user table for audit trail
[AuditTable]
public class User : EntityBase
{
    public string Username { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    
    // EntityBase already includes audit fields:
    // CreateTime, UpdateTime, CreateUserId, UpdateUserId, 
    // CreateUserName, UpdateUserName
}

// Mark order table for audit trail
[AuditTable]
public class Order : EntityBase
{
    public string OrderNumber { get; set; }
    public long UserId { get; set; }
    public decimal TotalAmount { get; set; }
    public DateTime OrderDate { get; set; }
}

// Audit interceptor example
public class AuditInterceptor : IDbCommandInterceptor
{
    private readonly ICurrentUserService _currentUserService;
    
    public AuditInterceptor(ICurrentUserService currentUserService)
    {
        _currentUserService = currentUserService;
    }
    
    public override InterceptionResult<int> NonQueryExecuting(
        DbCommand command, 
        CommandEventData eventData, 
        InterceptionResult<int> result)
    {
        var context = eventData.Context;
        var entries = context.ChangeTracker.Entries()
            .Where(e => e.Entity.GetType().GetCustomAttribute<AuditTableAttribute>() != null)
            .ToList();
            
        foreach (var entry in entries)
        {
            if (entry.Entity is IAuditableEntity auditableEntity)
            {
                var currentUser = _currentUserService.GetCurrentUser();
                var now = DateTime.UtcNow;
                
                switch (entry.State)
                {
                    case EntityState.Added:
                        auditableEntity.CreateTime = now;
                        auditableEntity.UpdateTime = now;
                        auditableEntity.CreateUserId = currentUser.Id;
                        auditableEntity.UpdateUserId = currentUser.Id;
                        auditableEntity.CreateUserName = currentUser.Username;
                        auditableEntity.UpdateUserName = currentUser.Username;
                        break;
                        
                    case EntityState.Modified:
                        auditableEntity.UpdateTime = now;
                        auditableEntity.UpdateUserId = currentUser.Id;
                        auditableEntity.UpdateUserName = currentUser.Username;
                        break;
                }
            }
        }
        
        return base.NonQueryExecuting(command, eventData, result);
    }
}
Cache Table Attribute (CacheTableAttribute)

Used to mark entity classes supporting caching strategies. The system automatically manages caching for these tables.

using GameFrameX.Foundation.Orm.Attribute;
using GameFrameX.Foundation.Orm.Entity;

// Mark config table for caching (config data changes infrequently, suitable for caching)
[CacheTable]
public class SystemConfig : EntityBase
{
    public string ConfigKey { get; set; }
    public string ConfigValue { get; set; }
    public string Description { get; set; }
    public string Category { get; set; }
}

// Mark dictionary table for caching (dictionary data is relatively stable, suitable for caching)
[CacheTable]
public class Dictionary : EntityBase
{
    public string DictType { get; set; }
    public string DictKey { get; set; }
    public string DictValue { get; set; }
    public string Description { get; set; }
    public int SortOrder { get; set; }
}

// Mark permission table for caching (permission data is accessed frequently but changes infrequently)
[CacheTable]
public class Permission : EntityBase
{
    public string PermissionCode { get; set; }
    public string PermissionName { get; set; }
    public string Description { get; set; }
    public string Module { get; set; }
}

// Cache service example
public class CacheService<T> where T : class
{
    private readonly IMemoryCache _memoryCache;
    private readonly IDbContext _dbContext;
    private readonly ILogger<CacheService<T>> _logger;
    
    public CacheService(IMemoryCache memoryCache, IDbContext dbContext, ILogger<CacheService<T>> logger)
    {
        _memoryCache = memoryCache;
        _dbContext = dbContext;
        _logger = logger;
    }
    
    public async Task<List<T>> GetAllAsync()
    {
        var entityType = typeof(T);
        var cacheAttribute = entityType.GetCustomAttribute<CacheTableAttribute>();
        
        if (cacheAttribute == null)
        {
            // Caching not supported, query directly from database
            return await _dbContext.Set<T>().ToListAsync();
        }
        
        var cacheKey = $"CacheTable_{entityType.Name}_All";
        
        if (_memoryCache.TryGetValue(cacheKey, out List<T> cachedData))
        {
            _logger.LogDebug($"Get data from cache: {cacheKey}");
            return cachedData;
        }
        
        // Query from database and cache
        var data = await _dbContext.Set<T>().ToListAsync();
        
        var cacheOptions = new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30), // 30min expiry
            SlidingExpiration = TimeSpan.FromMinutes(5), // 5min sliding expiry
            Priority = CacheItemPriority.Normal
        };
        
        _memoryCache.Set(cacheKey, data, cacheOptions);
        _logger.LogDebug($"Data cached: {cacheKey}, records: {data.Count}");
        
        return data;
    }
    
    public async Task InvalidateCacheAsync()
    {
        var entityType = typeof(T);
        var cacheKey = $"CacheTable_{entityType.Name}_All";
        
        _memoryCache.Remove(cacheKey);
        _logger.LogDebug($"Cache invalidated: {cacheKey}");
    }
}

// Cache manager example
public class CacheManager
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<CacheManager> _logger;
    
    public CacheManager(IServiceProvider serviceProvider, ILogger<CacheManager> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }
    
    public async Task RefreshAllCacheTablesAsync()
    {
        var assembly = Assembly.GetExecutingAssembly();
        var cacheTableTypes = assembly.GetTypes()
            .Where(t => t.GetCustomAttribute<CacheTableAttribute>() != null)
            .ToList();
            
        foreach (var type in cacheTableTypes)
        {
            try
            {
                var serviceType = typeof(CacheService<>).MakeGenericType(type);
                var service = _serviceProvider.GetService(serviceType);
                
                if (service != null)
                {
                    var invalidateMethod = serviceType.GetMethod("InvalidateCacheAsync");
                    await (Task)invalidateMethod.Invoke(service, null);
                    
                    var getAllMethod = serviceType.GetMethod("GetAllAsync");
                    await (Task)getAllMethod.Invoke(service, null);
                    
                    _logger.LogInformation($"Cache table {type.Name} refreshed");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error refreshing cache table {type.Name}");
            }
        }
    }
}
Soft Delete Attribute (SoftDeleteAttribute)

Marks entity classes that support soft delete functionality. Delete operations mark records as deleted instead of physical deletion.

using GameFrameX.Foundation.Orm.Attribute;
using GameFrameX.Foundation.Orm.Entity;

// Mark user table for soft delete
[SoftDelete]
public class User : EntityBase
{
    public string Username { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    
    // EntityBase already includes IsDelete field
}

// Mark article table for soft delete
[SoftDelete]
public class Article : EntityBase
{
    public string Title { get; set; }
    public string Content { get; set; }
    public long AuthorId { get; set; }
    public DateTime PublishTime { get; set; }
}

// Soft delete interceptor
public class SoftDeleteInterceptor : IDbCommandInterceptor
{
    public override InterceptionResult<int> NonQueryExecuting(
        DbCommand command, 
        CommandEventData eventData, 
        InterceptionResult<int> result)
    {
        var context = eventData.Context;
        
        // Handle soft delete entity delete operations
        var softDeleteEntries = context.ChangeTracker.Entries()
            .Where(e => e.State == EntityState.Deleted && 
                       e.Entity.GetType().GetCustomAttribute<SoftDeleteAttribute>() != null)
            .ToList();
            
        foreach (var entry in softDeleteEntries)
        {
            // Convert delete operation to update operation
            entry.State = EntityState.Modified;
            
            if (entry.Entity is EntityBase entityBase)
            {
                entityBase.IsDelete = true;
                entityBase.UpdateTime = DateTime.UtcNow;
                // Set update user info...
            }
        }
        
        return base.NonQueryExecuting(command, eventData, result);
    }
}

// Soft delete query filter
public static class SoftDeleteQueryExtensions
{
    public static IQueryable<T> WhereNotDeleted<T>(this IQueryable<T> query) 
        where T : class
    {
        var entityType = typeof(T);
        var softDeleteAttribute = entityType.GetCustomAttribute<SoftDeleteAttribute>();
        
        if (softDeleteAttribute != null && typeof(EntityBase).IsAssignableFrom(entityType))
        {
            return query.Where(e => !((EntityBase)(object)e).IsDelete);
        }
        
        return query;
    }
    
    public static IQueryable<T> IncludeDeleted<T>(this IQueryable<T> query) 
        where T : class
    {
        // Return query including deleted records
        return query;
    }
    
    public static IQueryable<T> OnlyDeleted<T>(this IQueryable<T> query) 
        where T : class
    {
        var entityType = typeof(T);
        var softDeleteAttribute = entityType.GetCustomAttribute<SoftDeleteAttribute>();
        
        if (softDeleteAttribute != null && typeof(EntityBase).IsAssignableFrom(entityType))
        {
            return query.Where(e => ((EntityBase)(object)e).IsDelete);
        }
        
        return query.Where(_ => false); // If soft delete is not supported, return empty result
    }
}

// Usage Examples
public class UserService
{
    private readonly ApplicationDbContext _context;
    
    public UserService(ApplicationDbContext context)
    {
        _context = context;
    }
    
    // Get active users (auto-filter deleted)
    public async Task<List<User>> GetActiveUsersAsync()
    {
        return await _context.Users
            .WhereNotDeleted()
            .ToListAsync();
    }
    
    // Get deleted users
    public async Task<List<User>> GetDeletedUsersAsync()
    {
        return await _context.Users
            .OnlyDeleted()
            .ToListAsync();
    }
    
    // Get all users (including deleted)
    public async Task<List<User>> GetAllUsersAsync()
    {
        return await _context.Users
            .IncludeDeleted()
            .ToListAsync();
    }
    
    // Soft delete user
    public async Task SoftDeleteUserAsync(long userId)
    {
        var user = await _context.Users.FindAsync(userId);
        if (user != null)
        {
            _context.Users.Remove(user); // Will be converted to soft delete by interceptor
            await _context.SaveChangesAsync();
        }
    }
    
    // Restore deleted user
    public async Task RestoreUserAsync(long userId)
    {
        var user = await _context.Users
            .IncludeDeleted()
            .FirstOrDefaultAsync(u => u.Id == userId);
            
        if (user != null && user.IsDelete)
        {
            user.IsDelete = false;
            user.UpdateTime = DateTime.UtcNow;
            await _context.SaveChangesAsync();
        }
    }
}
Version Control Attribute (VersionControlAttribute)

Marks entity classes that support data version management, implementing optimistic locking and concurrency control.

using GameFrameX.Foundation.Orm.Attribute;
using GameFrameX.Foundation.Orm.Entity;

// Mark user table for version control
[VersionControl]
public class User : EntityBase
{
    public string Username { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    
    // EntityBase already includes Version field
}

// Mark inventory table for version control (prevent overselling)
[VersionControl]
public class Inventory : EntityBase
{
    public string ProductId { get; set; }
    public int Quantity { get; set; }
    public int ReservedQuantity { get; set; }
    public decimal UnitCost { get; set; }
}

// Mark account balance table for version control (prevent concurrent balance errors)
[VersionControl]
public class AccountBalance : EntityBase
{
    public long UserId { get; set; }
    public decimal Balance { get; set; }
    public decimal FrozenAmount { get; set; }
    public string Currency { get; set; }
}

// Version control interceptor
public class VersionControlInterceptor : IDbCommandInterceptor
{
    public override InterceptionResult<int> NonQueryExecuting(
        DbCommand command, 
        CommandEventData eventData, 
        InterceptionResult<int> result)
    {
        var context = eventData.Context;
        
        // Handle version-controlled entity update operations
        var versionControlEntries = context.ChangeTracker.Entries()
            .Where(e => e.State == EntityState.Modified && 
                       e.Entity.GetType().GetCustomAttribute<VersionControlAttribute>() != null)
            .ToList();
            
        foreach (var entry in versionControlEntries)
        {
            if (entry.Entity is EntityBase entityBase)
            {
                // Auto-increment version number
                entityBase.Version++;
                
                // Mark Version field as modified
                entry.Property(nameof(EntityBase.Version)).IsModified = true;
            }
        }
        
        return base.NonQueryExecuting(command, eventData, result);
    }
}

// Version control service
public class VersionControlService<T> where T : EntityBase
{
    private readonly IDbContext _dbContext;
    private readonly ILogger<VersionControlService<T>> _logger;
    
    public VersionControlService(IDbContext dbContext, ILogger<VersionControlService<T>> logger)
    {
        _dbContext = dbContext;
        _logger = logger;
    }
    
    public async Task<T> UpdateWithVersionCheckAsync(long id, Action<T> updateAction, int maxRetries = 3)
    {
        var entityType = typeof(T);
        var versionControlAttribute = entityType.GetCustomAttribute<VersionControlAttribute>();
        
        if (versionControlAttribute == null)
        {
            throw new InvalidOperationException($"Entity type {entityType.Name} is not marked with VersionControlAttribute");
        }
        
        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            try
            {
                var entity = await _dbContext.Set<T>().FindAsync(id);
                if (entity == null)
                {
                    throw new EntityNotFoundException($"Entity {entityType.Name} (ID: {id}) not found");
                }
                
                var originalVersion = entity.Version;
                
                // Execute update operation
                updateAction(entity);
                
                // Set update time
                entity.UpdateTime = DateTime.UtcNow;
                
                // Save changes
                await _dbContext.SaveChangesAsync();
                
                _logger.LogDebug($"Entity {entityType.Name} (ID: {id}) updated successfully, version from {originalVersion} to {entity.Version}");
                return entity;
            }
            catch (DbUpdateConcurrencyException ex)
            {
                _logger.LogWarning($"Entity {entityType.Name} (ID: {id}) version conflict, attempt {attempt} retry");
                
                if (attempt == maxRetries)
                {
                    throw new ConcurrencyException($"Entity {entityType.Name} (ID: {id}) still has version conflicts after {maxRetries} retries", ex);
                }
                
                // Reload entity to get latest version
                _dbContext.Entry(await _dbContext.Set<T>().FindAsync(id)).Reload();
                
                // Wait before retrying
                await Task.Delay(TimeSpan.FromMilliseconds(100 * attempt));
            }
        }
        
        throw new InvalidOperationException("Should not reach here");
    }
}

// Usage Examples
public class InventoryService
{
    private readonly VersionControlService<Inventory> _versionControlService;
    private readonly ApplicationDbContext _context;
    
    public InventoryService(VersionControlService<Inventory> versionControlService, ApplicationDbContext context)
    {
        _versionControlService = versionControlService;
        _context = context;
    }
    
    // Reduce inventory (prevent overselling)
    public async Task<bool> ReduceInventoryAsync(string productId, int quantity)
    {
        var inventory = await _context.Inventories
            .FirstOrDefaultAsync(i => i.ProductId == productId);
            
        if (inventory == null)
        {
            throw new EntityNotFoundException($"Inventory record for product {productId} not found");
        }
        
        try
        {
            await _versionControlService.UpdateWithVersionCheckAsync(inventory.Id, inv =>
            {
                if (inv.Quantity < quantity)
                {
                    throw new InsufficientInventoryException($"Insufficient inventory: {inv.Quantity}, required: {quantity}");
                }
                
                inv.Quantity -= quantity;
            });
            
            return true;
        }
        catch (ConcurrencyException)
        {
            // Version conflict, likely due to concurrent operations
            throw new ConcurrencyException("Inventory update failed, please retry");
        }
    }
    
    // Add inventory
    public async Task AddInventoryAsync(string productId, int quantity)
    {
        var inventory = await _context.Inventories
            .FirstOrDefaultAsync(i => i.ProductId == productId);
            
        if (inventory == null)
        {
            throw new EntityNotFoundException($"Inventory record for product {productId} not found");
        }
        
        await _versionControlService.UpdateWithVersionCheckAsync(inventory.Id, inv =>
        {
            inv.Quantity += quantity;
        });
    }
}

// Account balance service example
public class AccountBalanceService
{
    private readonly VersionControlService<AccountBalance> _versionControlService;
    private readonly ApplicationDbContext _context;
    
    public AccountBalanceService(VersionControlService<AccountBalance> versionControlService, ApplicationDbContext context)
    {
        _versionControlService = versionControlService;
        _context = context;
    }
    
    // Deduct balance
    public async Task<bool> DeductBalanceAsync(long userId, decimal amount, string currency = "CNY")
    {
        var balance = await _context.AccountBalances
            .FirstOrDefaultAsync(b => b.UserId == userId && b.Currency == currency);
            
        if (balance == null)
        {
            throw new EntityNotFoundException($"Account for user {userId} ({currency}) not found");
        }
        
        try
        {
            await _versionControlService.UpdateWithVersionCheckAsync(balance.Id, bal =>
            {
                if (bal.Balance < amount)
                {
                    throw new InsufficientBalanceException($"Insufficient balance: {bal.Balance}, required: {amount}");
                }
                
                bal.Balance -= amount;
            });
            
            return true;
        }
        catch (ConcurrencyException)
        {
            throw new ConcurrencyException("Balance update failed, please retry");
        }
    }
    
    // Add balance
    public async Task AddBalanceAsync(long userId, decimal amount, string currency = "CNY")
    {
        var balance = await _context.AccountBalances
            .FirstOrDefaultAsync(b => b.UserId == userId && b.Currency == currency);
            
        if (balance == null)
        {
            throw new EntityNotFoundException($"Account for user {userId} ({currency}) not found");
        }
        
        await _versionControlService.UpdateWithVersionCheckAsync(balance.Id, bal =>
        {
            bal.Balance += amount;
        });
    }
}
Complete Integration Example
using GameFrameX.Foundation.Orm.Attribute;
using GameFrameX.Foundation.Orm.Entity;
using Microsoft.EntityFrameworkCore;

namespace MyApplication.Entities
{
    // User entity: audit, soft delete, version control
    [AuditTable]
    [SoftDelete]
    [VersionControl]
    public class User : EntityBase
    {
        public string Username { get; set; }
        public string Email { get; set; }
        public string PasswordHash { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime? LastLoginTime { get; set; }
    }
    
    // System config: caching, audit
    [CacheTable]
    [AuditTable]
    public class SystemConfig : EntityBase
    {
        public string ConfigKey { get; set; }
        public string ConfigValue { get; set; }
        public string Description { get; set; }
        public string Category { get; set; }
    }
    
    // Inventory: version control, audit
    [VersionControl]
    [AuditTable]
    public class Inventory : EntityBase
    {
        public string ProductId { get; set; }
        public int Quantity { get; set; }
        public int ReservedQuantity { get; set; }
        public decimal UnitCost { get; set; }
        public string WarehouseCode { get; set; }
    }
    
    // Order: audit, soft delete
    [AuditTable]
    [SoftDelete]
    public class Order : EntityBase
    {
        public string OrderNumber { get; set; }
        public long UserId { get; set; }
        public decimal TotalAmount { get; set; }
        public DateTime OrderDate { get; set; }
        public OrderStatus Status { get; set; }
        
        public virtual User User { get; set; }
        public virtual ICollection<OrderItem> OrderItems { get; set; }
    }
}

// DbContext configuration
public class ApplicationDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<SystemConfig> SystemConfigs { get; set; }
    public DbSet<Inventory> Inventories { get; set; }
    public DbSet<Order> Orders { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .AddInterceptors(
                new AuditInterceptor(),
                new SoftDeleteInterceptor(),
                new VersionControlInterceptor()
            );
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Add global query filter for all SoftDeleteAttribute-marked entities
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            var clrType = entityType.ClrType;
            if (clrType.GetCustomAttribute<SoftDeleteAttribute>() != null &&
                typeof(EntityBase).IsAssignableFrom(clrType))
            {
                var parameter = Expression.Parameter(clrType, "e");
                var property = Expression.Property(parameter, nameof(EntityBase.IsDelete));
                var condition = Expression.Equal(property, Expression.Constant(false));
                var lambda = Expression.Lambda(condition, parameter);
                
                modelBuilder.Entity(clrType).HasQueryFilter(lambda);
            }
        }
        
        base.OnModelCreating(modelBuilder);
    }
}

// Service registration
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(connectionString));
    
    services.AddScoped<AuditInterceptor>();
    services.AddScoped<SoftDeleteInterceptor>();
    services.AddScoped<VersionControlInterceptor>();
    
    services.AddScoped(typeof(CacheService<>));
    services.AddScoped(typeof(VersionControlService<>));
    services.AddScoped<CacheManager>();
    
    services.AddMemoryCache();
}

🖊️ Logger Library (GameFrameX.Foundation.Logger)

Serilog-based logging configuration tool providing simple and easy-to-use logging capabilities.

Features
  • Multiple log levels (Debug, Info, Warning, Error, Fatal)
  • Flexible output configuration
  • Custom log provider support
  • Log self-diagnostics
  • Pre-initialization logging: Use LogHelper directly without manual initialization
  • Auto log merging: Pre and post-initialization logs are automatically merged
Pre-initialization Logging

Before the formal logging system initializes, you can use LogHelper to output logs to the console. When LogHandler.Create() initializes the formal logger, previous temporary logs are automatically merged into the new logging system, ensuring no logs are lost.

class Program
{
    static void Main(string[] args)
    {
        // No initialization required, use LogHelper directly
        LogHelper.Info("Loading configuration...");
        LogHelper.Debug("Parameters: {Args}", string.Join(", ", args));
        LogHelper.Warning("Configuration not found, using defaults");

        // Initialize formal logging system
        var logger = LogHandler.Create(options);

        // Previous temporary logs have been auto-merged into the new logging system
        LogHelper.Info("System startup complete");
    }
}
Usage Examples
// Initialize logging
LogHandler.Create(LogOptions.Default);

// Log messages
LogHelper.Debug("Debug message");
LogHelper.Info("Info message");
LogHelper.Warning("Warning message");
LogHelper.Error("Error message");
LogHelper.Fatal("Fatal error");

⚙️ CLI Options (GameFrameX.Foundation.Options)

A powerful command-line argument and environment variable parsing library that automatically maps to strongly-typed configuration objects.

Features
  • Argument priority: Command-line args > Environment variables > Defaults
  • Generic support: Supports any strongly-typed configuration class
  • Multiple launch modes: Supports Docker, exe, shell, etc.
  • Auto prefix handling: Automatically adds -- prefix to arguments
  • Boolean parameter support: Supports multiple boolean formats
  • Environment variable mapping: Auto-maps env vars to config properties
  • Type conversion: Auto-converts string arguments to target types
  • Attribute support: Rich configuration attributes
Core Components
Component FunctionDescription
CommandLineArgumentConverter Command-line argument converter, core argument processing
OptionsBuilder<T> Configuration builder for constructing generic config objects
OptionsProvider Configuration provider for managing config objects
Quick Start
1. Define Configuration Class
public class AppConfig
{
    public string Host { get; set; } = "localhost";
    public int Port { get; set; } = 8080;
    public bool Debug { get; set; } = false;
    public string LogLevel { get; set; } = "info";
    public double Timeout { get; set; } = 30.5;
}
2. Using OptionsBuilder
using GameFrameX.Foundation.Options;

class Program
{
    static void Main(string[] args)
    {
        // Create options builder
        var builder = new OptionsBuilder<AppConfig>(args);
        
        // Build configuration object
        var config = builder.Build();
        
        // Use configuration
        Console.WriteLine($"Server: {config.Host}:{config.Port}");
        Console.WriteLine($"Debug mode: {config.Debug}");
        Console.WriteLine($"Log level: {config.LogLevel}");
        Console.WriteLine($"Timeout: {config.Timeout}s");
    }
}
Usage
Command-Line Arguments

Supports multiple argument formats:

# Key-value format
myapp.exe --host=example.com --port=9090 --debug=true

# Separate format
myapp.exe --host example.com --port 9090 --debug true

# Boolean flag format
myapp.exe --host example.com --port 9090 --debug

# Mixed format
myapp.exe --host=example.com --port 9090 --debug
Environment Variables
# Set environment variables
export HOST=example.com
export PORT=9090
export DEBUG=true

# Run the program
myapp.exe
Docker Support
# Dockerfile
FROM mcr.microsoft.com/dotnet/runtime:8.0
COPY . /app
WORKDIR /app
ENTRYPOINT ["dotnet", "MyApp.dll"]
# Docker run
docker run myapp --host example.com --port 9090 --debug

# Or use environment variables
docker run -e HOST=example.com -e PORT=9090 -e DEBUG=true myapp
Advanced Features
Using Attribute Configuration
using GameFrameX.Foundation.Options.Attributes;

public class AdvancedConfig
{
    [Option("h", "host", Required = false, DefaultValue = "localhost")]
    [HelpText("Server host address")]
    public string Host { get; set; }

    [Option("p", "port", Required = true)]
    [HelpText("Server port")]
    public int Port { get; set; }

    [FlagOption("d", "debug")]
    [HelpText("Enable debug mode")]
    public bool Debug { get; set; }

    [RequiredOption("api-key", Required = true)]
    [EnvironmentVariable("API_KEY")]
    [HelpText("API key")]
    public string ApiKey { get; set; }

    [DefaultValue(30.0)]
    public double Timeout { get; set; }
}
Builder Options
var builder = new OptionsBuilder<AppConfig>(
    args: args,
    boolFormat: BoolArgumentFormat.Flag,        // Boolean parameter format
    ensurePrefixedKeys: true,                   // Ensure parameters have prefix
    useEnvironmentVariables: true              // Use environment variables
);

var config = builder.Build(skipValidation: false); // Skip validation or not
Argument Priority

Parameters are applied with the following priority (higher priority overrides lower):

  1. Command-line arguments (highest priority)
  2. Environment variables
  3. Default values (lowest priority)
Example
public class Config
{
    public string Host { get; set; } = "localhost";  // Default value
    public int Port { get; set; } = 8080;           // Default value
}
# Set environment variables
export HOST=env.example.com
export PORT=7070

# Run the program(command-line args override environment variables)
myapp.exe --host cmd.example.com

# Result:
# Host = "cmd.example.com"  (from command-line args)
# Port = 7070               (from environment variables)
Boolean Options

Supports multiple boolean parameter formats:

# Flag format (recommended)
myapp.exe --debug                    # debug = true

# Key-value format
myapp.exe --debug=true               # debug = true
myapp.exe --debug=false              # debug = false

# Separate format
myapp.exe --debug true               # debug = true
myapp.exe --debug false              # debug = false

# Supported boolean values
true, false, 1, 0, yes, no, on, off
Type Conversion

Automatically supports the following type conversions:

  • string - Direct use
  • int, int? - Integer conversion
  • bool, bool? - Boolean conversion
  • double, double? - Double conversion
  • float, float? - Float conversion
  • decimal, decimal? - Decimal conversion
  • DateTime, DateTime? - DateTime conversion
  • Guid, Guid? - GUID conversion
  • Enum - Enum conversion
Example
public class TypedConfig
{
    public int Port { get; set; }
    public bool Debug { get; set; }
    public DateTime StartTime { get; set; }
    public LogLevel Level { get; set; }  // Enum
}

public enum LogLevel
{
    Debug, Info, Warning, Error
}
myapp.exe --port 9090 --debug true --start-time "2024-01-01 10:00:00" --level Info
Error Handling
Required Parameter Validation
public class Config
{
    [RequiredOption("api-key", Required = true)]
    public string ApiKey { get; set; }
}

If a required parameter is missing, an ArgumentException is thrown:

Missing required option: api-key
Type Conversion Errors

When a parameter value cannot be converted to the target type, the default value is used and a warning is displayed.

Best Practices
1. Configuration Class Design
public class AppConfig
{
    // Use meaningful default values
    public string Host { get; set; } = "localhost";
    public int Port { get; set; } = 8080;
    
    // Boolean properties default to false
    public bool Debug { get; set; } = false;
    
    // Use attributes for additional info
    [RequiredOption("database-url", Required = true)]
    [EnvironmentVariable("DATABASE_URL")]
    public string DatabaseUrl { get; set; }
}
2. Error Handling
try
{
    var builder = new OptionsBuilder<AppConfig>(args);
    var config = builder.Build();
    
    // Use configuration to start app
    StartApplication(config);
}
catch (ArgumentException ex)
{
    Console.WriteLine($"Configuration error: {ex.Message}");
    Environment.Exit(1);
}
3. Docker Integration
// Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        var builder = new OptionsBuilder<AppConfig>(args);
        var config = builder.Build();
        
        // In Docker, typically use environment variables
        // In development, typically use command-line args
        
        var app = CreateApplication(config);
        app.Run();
    }
}
# docker-compose.yml
version: '3.8'
services:
  myapp:
    image: myapp:latest
    environment:
      - HOST=0.0.0.0
      - PORT=8080
      - DEBUG=false
    command: [ "--log-level", "info" ]
Complete Example
using GameFrameX.Foundation.Options;
using GameFrameX.Foundation.Options.Attributes;

namespace MyApp
{
    public class ServerConfig
    {
        [Option("h", "host", DefaultValue = "localhost")]
        [EnvironmentVariable("SERVER_HOST")]
        [HelpText("Server host address")]
        public string Host { get; set; }

        [Option("p", "port", DefaultValue = 8080)]
        [EnvironmentVariable("SERVER_PORT")]
        [HelpText("Server port")]
        public int Port { get; set; }

        [FlagOption("d", "debug")]
        [EnvironmentVariable("DEBUG")]
        [HelpText("Enable debug mode")]
        public bool Debug { get; set; }

        [RequiredOption("database-url", Required = true)]
        [EnvironmentVariable("DATABASE_URL")]
        [HelpText("Database connection string")]
        public string DatabaseUrl { get; set; }

        [Option("timeout", DefaultValue = 30.0)]
        [EnvironmentVariable("REQUEST_TIMEOUT")]
        [HelpText("Request timeout (seconds)")]
        public double Timeout { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var builder = new OptionsBuilder<ServerConfig>(args);
                var config = builder.Build();

                Console.WriteLine("Server configuration:");
                Console.WriteLine($"  Host: {config.Host}");
                Console.WriteLine($"  Port: {config.Port}");
                Console.WriteLine($"  Debug: {config.Debug}");
                Console.WriteLine($"  Database: {config.DatabaseUrl}");
                Console.WriteLine($"  Timeout: {config.Timeout}s");

                // Start server
                StartServer(config);
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine($"Configuration error: {ex.Message}");
                ShowHelp();
                Environment.Exit(1);
            }
        }

        static void StartServer(ServerConfig config)
        {
            // Server startup logic
            Console.WriteLine($"Server started at {config.Host}:{config.Port}");
        }

        static void ShowHelp()
        {
            Console.WriteLine("Usage:");
            Console.WriteLine("  myapp.exe --host <host> --port <port> --database-url <db-url> [options]");
            Console.WriteLine();
            Console.WriteLine("Options:");
            Console.WriteLine("  -h, --host <host>           Server host address (default: localhost)");
            Console.WriteLine("  -p, --port <port>           Server port (default: 8080)");
            Console.WriteLine("  -d, --debug                 Enable debug mode");
            Console.WriteLine("      --database-url <URL>    Database connection string (required)");
            Console.WriteLine("      --timeout <seconds>     Request timeout (default: 30.0)");
        }
    }
}
CommandLineArgumentConverter Usage

In addition to OptionsBuilder, you can also use the underlying CommandLineArgumentConverter directly:

using GameFrameX.Foundation.Options;

// Create converter instance
var converter = new CommandLineArgumentConverter();

// Raw command-line arguments
var args = new[] { "--port", "8080", "-h", "localhost" };

// Set environment variables (optional)
Environment.SetEnvironmentVariable("APP_NAME", "MyApplication");
Environment.SetEnvironmentVariable("LOG_LEVEL", "debug-mode");

// Convert to standard format (merge args and env vars)
var standardArgs = converter.ConvertToStandardFormat(args);
// Result: ["--port", "8080", "-h", "localhost", "--APP_NAME", "MyApplication", "--LOG_LEVEL", "debugmode"]

// Convert to command-line string
var commandLineString = converter.ToCommandLineString(standardArgs);
// Result: "--port 8080 -h localhost --APP_NAME MyApplication --LOG_LEVEL debugmode"

// Get all environment variables
var envVars = converter.GetEnvironmentVariables();
Console.WriteLine($"Detected {envVars.Count} environment variables");
Boolean Parameter Support

CommandLineArgumentConverter supports intelligent recognition and processing of boolean parameters with three formats:

using GameFrameX.Foundation.Options;

// Set boolean environment variables
Environment.SetEnvironmentVariable("ENABLE_LOGGING", "true");
Environment.SetEnvironmentVariable("DEBUG_MODE", "false");
Environment.SetEnvironmentVariable("VERBOSE", "yes");

var converter = new CommandLineArgumentConverter();

// 1. Flag format (default) - only add flags for true values
converter.BoolFormat = BoolArgumentFormat.Flag;
var flagArgs = converter.ConvertToStandardFormat(Array.Empty<string>());
// Result: ["--ENABLE_LOGGING", "--VERBOSE"] (only true values)

// 2. Key-value format - add key-value pairs
converter.BoolFormat = BoolArgumentFormat.KeyValue;
var keyValueArgs = converter.ConvertToStandardFormat(Array.Empty<string>());
// Result: ["--ENABLE_LOGGING", "true", "--DEBUG_MODE", "false", "--VERBOSE", "true"]

// 3. Separated format - key and value separated
converter.BoolFormat = BoolArgumentFormat.Separated;
var separatedArgs = converter.ConvertToStandardFormat(Array.Empty<string>());
// Result: ["--ENABLE_LOGGING", "true", "--DEBUG_MODE", "false", "--VERBOSE", "true"]

Supported boolean value formats:

  • True values: "true", "1", "yes", "on", "enabled" (case-insensitive)
  • False values: "false", "0", "no", "off", "disabled" (case-insensitive)

🛠️ Utility Classes (GameFrameX.Foundation.Utility)

Provides a collection of practical utility classes including console operations, environment management, time processing, and Snowflake ID generation.

Core Components Overview
Component File Description
Console Helper ConsoleHelper.cs Console logo printing and formatted output
Environment Helper EnvironmentHelper.cs Environment variable management and type definitions
Time Helper TimerHelper.cs Unix timestamp processing and time conversion
Snowflake ID SnowFlakeIdHelper.cs Distributed unique ID generator (Snowflake algorithm implementation)
Console Helper
using GameFrameX.Foundation.Utility;

// Print application logo
ConsoleHelper.PrintLogo();
// Print formatted console logo for app startup branding
Environment Management
using GameFrameX.Foundation.Utility;

// Get current environment type
string currentEnv = Environments.Development;
Console.WriteLine($"Current environment: {currentEnv}");

// Environment check
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == Environments.Development)
{
    // Development-specific logic
    Console.WriteLine("Running in development environment");
}
Time Processing
using GameFrameX.Foundation.Utility;

// Unix timestamp constants
DateTime epochLocal = TimerHelper.EpochLocal;   // Unix epoch in local timezone
DateTime epochUtc = TimerHelper.EpochUtc;       // Unix epoch in UTC timezone

// Get current Unix timestamp (seconds)
long unixSeconds = TimerHelper.UnixTimeSeconds();
Console.WriteLine($"Current Unix timestamp (seconds): {unixSeconds}");

// Get current Unix timestamp (milliseconds)
long unixMilliseconds = TimerHelper.UnixTimeMilliseconds();
Console.WriteLine($"Current Unix timestamp (milliseconds): {unixMilliseconds}");

// Timestamp conversion example
DateTime currentTime = DateTime.UtcNow;
long timestamp = ((DateTimeOffset)currentTime).ToUnixTimeSeconds();
DateTime restored = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime;
Snowflake ID Generator
using GameFrameX.Foundation.Utility;

// Generate ID with default config
long id1 = SnowFlakeIdHelper.GenerateId();
long id2 = SnowFlakeIdHelper.GenerateId();
Console.WriteLine($"Generated IDs: {id1}, {id2}");

// Configure worker ID and data center ID
SnowFlakeIdHelper.WorkId = 1;        // Worker ID (0-31)
SnowFlakeIdHelper.DataCenterId = 1;  // Data center ID (0-31)

// Generate configured ID
long configuredId = SnowFlakeIdHelper.GenerateId();
Console.WriteLine($"Configured ID: {configuredId}");

// Get timestamp related info
DateTime utcStart = SnowFlakeIdHelper.UtcTimeStart;  // UTC start time
long epochTime = SnowFlakeIdHelper.EpochTime;        // Epoch timestamp

Console.WriteLine($"Snowflake ID start time: {utcStart}");
Console.WriteLine($"Epoch timestamp: {epochTime}");
Snowflake ID Algorithm Details

Snowflake ID is an open-source distributed ID generation algorithm by Twitter with the following characteristics:

  • Globally unique: Ensures ID uniqueness in distributed environments
  • Trend increasing: Generated IDs increase roughly by time, benefiting database indexing
  • High performance: Single machine can generate millions of IDs per second
  • No dependencies: Does not depend on databases or other external systems

ID structure (64 bits):

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
|   |                                             |   |       |       |
|   |<-------------- 41-bit timestamp ---------------->|   |<-5-bit->|<-5-bit->|<--12-bit-->
|                                                 |           |       |
Sign bit (1-bit)                                        |      Data center ID   Sequence
                                                  |      (5-bit)      (12-bit)
                                               Worker ID
                                                (5-bit)
  • 1-bit sign bit: Always 0
  • 41-bit timestamp: Millisecond precision, ~69 years of use
  • 5-bit Data center ID: Supports 32 data centers
  • 5-bit Worker ID: Each data center supports 32 worker nodes
  • 12-bit Sequence: Supports 4096 IDs per millisecond
Complete Usage Examples
using GameFrameX.Foundation.Utility;

namespace MyApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            // Print application logo
            ConsoleHelper.PrintLogo();
            
            // Check runtime environment
            string env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? Environments.Development;
            Console.WriteLine($"Current environment: {env}");
            
            // Configure Snowflake ID generator
            SnowFlakeIdHelper.WorkId = 1;
            SnowFlakeIdHelper.DataCenterId = 1;
            
            // Generate unique IDs
            for (int i = 0; i < 5; i++)
            {
                long id = SnowFlakeIdHelper.GenerateId();
                long timestamp = TimerHelper.UnixTimeMilliseconds();
                
                Console.WriteLine($"ID: {id}, Timestamp: {timestamp}");
                
                // Brief delay to observe ID changes
                Thread.Sleep(1);
            }
            
            // Time processing example
            Console.WriteLine($"Unix epoch (UTC): {TimerHelper.EpochUtc}");
            Console.WriteLine($"Unix epoch (local): {TimerHelper.EpochLocal}");
            Console.WriteLine($"Current Unix timestamp (seconds): {TimerHelper.UnixTimeSeconds()}");
            Console.WriteLine($"Current Unix timestamp (ms): {TimerHelper.UnixTimeMilliseconds()}");
        }
    }
}

🧪 Testing

The project includes comprehensive unit tests ensuring code quality and functional correctness. All core features have corresponding test cases with over 95% test coverage.

Test Coverage

🧩 Extension Method Library Tests (Extensions)
  • ArgumentAlreadyExceptionTests: Argument already exists exception tests
  • BidirectionalDictionaryTests: Bidirectional dictionary tests
  • ByteExtensionTests: Byte extension method tests
  • CollectionExtensionsTests: Collection extension method tests
  • ConcurrentLimitedQueueTests: Concurrent limited queue tests
  • DisposableConcurrentDictionaryTests: Disposable concurrent dictionary tests
  • DisposableDictionaryTests: Disposable dictionary tests
  • IDictionaryExtensionsTests: Dictionary extension method tests
  • IEnumerableExtensionsTests: Enumerable extension method tests
  • ListExtensionsTests: List extension method tests
  • LookupXTests: Lookup table tests
  • NullObjectTests: Null object pattern tests
  • NullableConcurrentDictionaryTests: Nullable concurrent dictionary tests
  • NullableDictionaryTests: Nullable dictionary tests
  • ObjectExtensionsTests: Object extension method tests
  • ReadOnlySpanExtensionsTests: ReadOnlySpan extension tests
  • SequenceReaderExtensionsTests: Sequence reader extension tests
  • SpanExtensionsTests: Span extension method tests
  • StringExtensionsTests: String extension method tests
  • TypeExtensionsTests: Type extension method tests
🔐 Encryption Library Tests (Encryption)
  • AesHelperTests: AES encryption algorithm tests
  • DsaHelperTests: DSA digital signature tests
  • RsaHelperTests: RSA encryption algorithm tests
  • Sm2HelperTests: SM2 national crypto algorithm tests
  • Sm4HelperTests: SM4 national crypto algorithm tests
  • XorHelperTests: XOR encryption tests
🌐 Localization Framework Tests (Localization)
  • LocalizationServiceTests: Localization service core tests
    • Singleton pattern validation tests
    • Localized string retrieval tests
    • Parameterized message formatting tests
    • Unknown key handling tests
    • Thread safety concurrency tests
  • ResourceManagerTests: Resource manager tests - Provider priority tests
    • Lazy loading mechanism tests
    • Statistics validation tests
  • DefaultResourceProviderTests: Default resource provider tests
  • AssemblyResourceProviderTests: Assembly resource provider tests
    • .resx file loading tests
    • Multi-culture support tests
    • Resource caching mechanism tests
🔗 Hash Library Tests (Hash)
  • CrcHelperTests: CRC checksum algorithm tests
  • HmacSha256HelperTests: HMAC-SHA256 tests
  • Md5HelperTests: MD5 hash algorithm tests
  • MurmurHash3HelperTests: MurmurHash3 algorithm tests
  • Sha1HelperTests: SHA-1 hash algorithm tests
  • Sha256HelperTests: SHA-256 hash algorithm tests
  • Sha512HelperTests: SHA-512 hash algorithm tests
  • XxHashHelperTests: xxHash high performance hash tests
🌐 HTTP Extension Library Tests (Http.Extension)
  • HttpExtensionTests: HTTP client extension method tests
⚙️ CLI Options Tests (Options)
  • CommandLineArgumentConverterTests: Command-line argument converter tests
    • Empty argument array handling tests
    • Empty argument value handling tests
    • Duplicate argument detection tests
    • Environment variable conversion tests
    • Value cleanup tests
    • Single hyphen argument conversion tests
    • Command-line string generation tests
    • Environment variable retrieval tests
    • Full workflow tests
    • Boolean options tests
      • Flag format boolean parameter tests
      • Key-value format boolean parameter tests
      • Separator format boolean parameter tests
      • Multiple boolean value format parsing tests
      • Non-boolean value handling tests
  • OptionsBuilderTests: Options builder tests
    • Basic configuration build tests
    • Attribute configuration tests
    • Type conversion tests
    • Validation tests
  • OptionsProviderTests: Options provider tests
    • Configuration registration and retrieval tests
    • Global configuration management tests

Run tests

# Run all tests
dotnet test

# Run specific module tests
dotnet test --filter "FullyQualifiedName~Extensions"
dotnet test --filter "FullyQualifiedName~Encryption"
dotnet test --filter "FullyQualifiedName~Hash"
dotnet test --filter "FullyQualifiedName~Localization"
dotnet test --filter "FullyQualifiedName~Options"

# Run specific test class
dotnet test --filter "ClassName=XxHashHelperTests"
dotnet test --filter "ClassName=StringExtensionsTests"
dotnet test --filter "ClassName=LocalizationServiceTests"
dotnet test --filter "ClassName=CommandLineArgumentConverterTests"

# Generate test coverage report
dotnet test --collect:"XPlat Code Coverage"

# Run performance tests
dotnet test --filter "Category=Performance"

Test Characteristics

  • Full coverage: Every public method has corresponding test cases
  • Edge case testing: Tests for null, boundary values, and exceptions
  • Performance testing: Benchmarks for key algorithms
  • Concurrency testing: Verifies thread-safe components under multi-threaded conditions
  • Compatibility testing: Ensures compatibility across .NET versions

🏗️ Architecture

Design Principles

  • High performance: All components are performance-optimized for high-concurrency scenarios
  • Ease of use: Clean API design to reduce learning curve
  • Extensible: Modular design, supports custom extensions
  • Type safety: Leverages .NET type system to reduce runtime errors
  • Memory efficient: Uses modern .NET features like Span<T> and Memory<T> to reduce allocations

Dependencies

GameFrameX.Foundation.Extensions (Core Extensions)
├── GameFrameX.Foundation.Encryption (Encryption)
├── GameFrameX.Foundation.Hash (Hash)
├── GameFrameX.Foundation.Json (JSON)
├── GameFrameX.Foundation.Logger (Logger)
├── GameFrameX.Foundation.Options (Options)
├── GameFrameX.Foundation.Http.Extension (HTTP Extensions)
└── GameFrameX.Foundation.Http.Normalization (HTTP Normalization)

🔧 Development Guide

Environment Requirements

  • .NET 10.0 or higher
  • C# 12.0 or higher

Build the project

# Clone the repository
git clone https://github.com/GameFrameX/GameFrameX.Foundation.git
cd GameFrameX.Foundation

# Restore dependencies
dotnet restore

# Build the project
dotnet build

# Run tests
dotnet test

Contributing Guide

  1. Fork this repository
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

📊 Performance Benchmarks

Extension Method Performance

Operation Traditional Extension Method Improvement
String null check 100ns 15ns 85%
Collection random element 200ns 50ns 75%
Span byte operations 500ns 80ns 84%
Bidirectional dict lookup 150ns 120ns 20%

Encryption Algorithm Performance

Algorithm Data Size Encrypt Time Decrypt Time
AES-256 1KB 0.05ms 0.04ms
RSA-2048 1KB 2.1ms 0.8ms
SM4 1KB 0.08ms 0.07ms
XOR 1KB 0.01ms 0.01ms

Hash Algorithm Performance

Algorithm Data Size Process Time Throughput
MD5 1MB 2.1ms 476MB/s
SHA-256 1MB 3.8ms 263MB/s
xxHash64 1MB 0.8ms 1.25GB/s
MurmurHash3 1MB 1.2ms 833MB/s

📋 System Requirements

  • .NET 10.0 or higher
  • Supports Windows, Linux, macOS

🤝 Contributing

Issues and Pull Requests are welcome to improve the project.

  1. Fork the project
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

🤝 Community Support

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgements

Thanks to all developers who have contributed to GameFrameX.Foundation!


<div align="center">

⬆ Back to Top

Made with ❤️ by GameFrameX Team

</div>

Product 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 is compatible.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on GameFrameX.Foundation.Options:

Package Downloads
GameFrameX.StartUp

GameFrameX.Setting,GameFrameX 框架的基础设施框架库.框架文档主页: https://gameframex.doc.alianblank.com

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.4.3 0 5/7/2026
2.4.2 105 4/28/2026
2.4.1 94 4/28/2026
2.2.10 198 4/2/2026
2.2.9 90 4/2/2026
2.2.8 101 3/22/2026
2.2.7 95 3/20/2026
2.2.6 88 3/19/2026
2.2.5 135 3/17/2026
2.2.4 102 3/17/2026
2.2.2 161 2/4/2026
2.2.1 106 2/4/2026
2.1.2 123 1/28/2026
2.1.1.2 117 1/22/2026
2.1.1.1 116 1/22/2026
2.1.0.1 117 1/22/2026
2.0.0.2 123 1/2/2026
Loading failed