SmartWorkz.Shared 1.0.1

dotnet add package SmartWorkz.Shared --version 1.0.1
                    
NuGet\Install-Package SmartWorkz.Shared -Version 1.0.1
                    
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="SmartWorkz.Shared" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SmartWorkz.Shared" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="SmartWorkz.Shared" />
                    
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 SmartWorkz.Shared --version 1.0.1
                    
#r "nuget: SmartWorkz.Shared, 1.0.1"
                    
#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 SmartWorkz.Shared@1.0.1
                    
#: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=SmartWorkz.Shared&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=SmartWorkz.Shared&version=1.0.1
                    
Install as a Cake Tool

SmartWorkz.Shared Library

NuGet Build Status License

Overview

SmartWorkz.Shared is a comprehensive utility library designed to streamline common development tasks across multiple projects and applications. It provides a collection of robust, performance-optimized components for various integration scenarios.

Library Components

The SmartWorkz.Shared library includes several specialized modules:

API Integration

  • ApiClient: High-performance HTTP client for RESTful API integration
  • ApiClientFactory: Support for working with multiple API endpoints
  • OAuth Helpers: Simplified OAuth authentication flows

Data Access

  • MongoDb: Simplified MongoDB access and query capabilities
  • ADO.NET: Enhanced data access with connection pooling and retry logic
  • Entity Framework: Additional extensions and utilities

File Transfer

  • FTP Client: Modern FTP/FTPS implementation with async support
  • SFTP Client: Secure file transfer over SSH
  • File Processing: Utilities for batch processing and transformations

Messaging

  • Email Service: SMTP email sending with templates and attachments
  • SMS Service: Text message integration with major providers
  • Push Notifications: Mobile push notification support

Common Utilities

  • Encryption: Secure data protection and hashing
  • Logging: Consistent logging abstractions
  • Caching: Multi-level caching framework
  • Configuration: Enhanced config management
  • Serialization: JSON, XML, and binary serialization helpers

Installation

Package Manager Console

Install-Package SmartWorkz.Shared

.NET CLI

dotnet add package SmartWorkz.Shared

Individual Components

You can also install specific components separately:

Install-Package SmartWorkz.Shared.ApiClient
Install-Package SmartWorkz.Shared.Data.MongoDB
Install-Package SmartWorkz.Shared.FileTransfer
Install-Package SmartWorkz.Shared.Messaging

Table of Contents


Enhanced ConfigHelper Documentation

Overview

The ConfigHelper class provides a robust solution for accessing configuration values in .NET applications. It features built-in caching with automatic invalidation on configuration changes, support for custom expiration times, and strongly-typed access with fallback defaults.

Features

  • Strongly-typed configuration access with default fallback values
  • Intelligent caching for optimal performance
  • Automatic cache invalidation when configuration changes
  • Custom expiration times for cached values
  • Thread-safe implementation for concurrent access
  • Manual cache management options
  • Diagnostic tools for monitoring cache size

Installation

The ConfigHelper is part of the SmartWorkz.Shared.Core namespace. To use it in your project:

dotnet add package SmartWorkz.Shared

Basic Usage

using Microsoft.Extensions.Configuration;
using SmartWorkz.Shared.Core;

// Standard usage with automatic caching
string apiUrl = ConfigHelper.Get<string>(configuration, "Api:BaseUrl", "https://default.com");

// Access with custom expiration time
int timeout = ConfigHelper.GetWithExpiration<int>(
    configuration, 
    "Api:TimeoutSeconds", 
    30,
    TimeSpan.FromMinutes(10)
);

// Boolean values
bool enableCache = ConfigHelper.Get<bool>(configuration, "Cache:Enabled", true);

// Date and time
DateTime expiryDate = ConfigHelper.Get<DateTime>(
    configuration, 
    "License:ExpirationDate", 
    DateTime.UtcNow.AddYears(1)
);

Caching Behavior

How Caching Works

  1. When requesting a configuration value, ConfigHelper first checks its internal cache
  2. If the value exists in cache and hasn't expired, it returns the cached value
  3. If the value isn't cached or has expired, it reads from the configuration provider
  4. The new value is then cached (with optional expiration) for future requests
  5. When configuration changes are detected, the cache is automatically cleared

Cache Keys

Cache keys are constructed using both the configuration key and the requested type:

"{configKey}:{typeName}"

This allows different types to be cached separately for the same configuration key.

Advanced Features

Custom Cache Expiration

You can specify custom expiration times for frequently changing values:

// Cache for 5 minutes
var quota = ConfigHelper.GetWithExpiration<int>(
    configuration, 
    "Api:RemainingQuota", 
    1000,
    TimeSpan.FromMinutes(5)
);

// Cache for 1 hour
var connectionString = ConfigHelper.GetWithExpiration<string>(
    configuration, 
    "Database:ConnectionString", 
    "Server=localhost;Database=master;Trusted_Connection=True;",
    TimeSpan.FromHours(1)
);

// Cache until midnight
var expiryTime = DateTime.Today.AddDays(1) - DateTime.Now;
var dailyLimit = ConfigHelper.GetWithExpiration<int>(
    configuration, 
    "Api:DailyLimit", 
    10000,
    expiryTime
);

Automatic Reload Support

The ConfigHelper automatically registers for configuration change notifications and clears the cache when changes are detected:

// In Program.cs
var builder = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .Build();

// When appsettings.json changes, ConfigHelper will automatically clear its cache

Manual Cache Management

You can manually manage the cache when needed:

// Clear the entire cache
ConfigHelper.ClearCache();

// Remove specific section from cache
ConfigHelper.RemoveFromCache("Database");

// Clean up expired entries only
ConfigHelper.CleanExpiredEntries();

// Check current cache size
int cacheSize = ConfigHelper.CacheCount;
Console.WriteLine($"Cache contains {cacheSize} items");

Best Practices

Configuration Key Organization

Group related settings with consistent key prefixes:

// Database settings
var connectionString = ConfigHelper.Get<string>(configuration, "Database:ConnectionString", "default");
var commandTimeout = ConfigHelper.Get<int>(configuration, "Database:CommandTimeout", 30);

// API settings
var apiKey = ConfigHelper.Get<string>(configuration, "Api:Key", "");
var apiBaseUrl = ConfigHelper.Get<string>(configuration, "Api:BaseUrl", "https://api.default.com");

Expiration Strategies

Choose appropriate expiration times based on the nature of the configuration:

  • Static configuration: No expiration needed (use standard Get<T> method)
  • Semi-dynamic configuration: Longer expiration (hours/days)
  • Frequently changing values: Short expiration (minutes)
  • Real-time values: Very short expiration (seconds) or no caching

Memory Management

For applications with many configuration values:

  • Periodically call CleanExpiredEntries() to remove expired cache entries
  • Monitor the cache size with CacheCount property
  • Consider adding a background job to clean the cache regularly

Common Scenarios

Working with Connection Strings

var connectionString = ConfigHelper.Get<string>(
    configuration, 
    "ConnectionStrings:DefaultConnection", 
    "Server=localhost;Database=master;Trusted_Connection=True;"
);

Application Settings

var appName = ConfigHelper.Get<string>(configuration, "AppSettings:Name", "Default App");
var maxUsers = ConfigHelper.Get<int>(configuration, "AppSettings:MaxUsers", 100);
var isProduction = ConfigHelper.Get<bool>(configuration, "AppSettings:IsProduction", false);

Feature Flags

bool isFeatureEnabled = ConfigHelper.GetWithExpiration<bool>(
    configuration, 
    "Features:NewReporting", 
    false,
    TimeSpan.FromMinutes(5)  // Check for updates every 5 minutes
);

if (isFeatureEnabled)
{
    // Implement new feature
}

API Settings with Fallbacks

var settings = new ApiSettings
{
    BaseUrl = ConfigHelper.Get<string>(configuration, "Api:BaseUrl", "https://api.default.com"),
    Timeout = ConfigHelper.Get<int>(configuration, "Api:TimeoutSeconds", 30),
    RetryCount = ConfigHelper.Get<int>(configuration, "Api:RetryCount", 3),
    ApiKey = ConfigHelper.Get<string>(configuration, "Api:Key", "")
};

Performance Tuning

Monitoring and Diagnostics

public class ConfigMonitor
{
    private readonly ILogger<ConfigMonitor> _logger;
    
    public ConfigMonitor(ILogger<ConfigMonitor> logger)
    {
        _logger = logger;
    }
    
    public void LogCacheStatistics()
    {
        int cacheCount = ConfigHelper.CacheCount;
        _logger.LogInformation("Configuration cache contains {Count} items", cacheCount);
        
        // Clean expired entries
        ConfigHelper.CleanExpiredEntries();
        
        int newCount = ConfigHelper.CacheCount;
        if (newCount < cacheCount)
        {
            _logger.LogInformation("Removed {Count} expired cache entries", cacheCount - newCount);
        }
    }
}

Strategic Cache Expiration

For optimal performance, consider these cache duration strategies:

  • High-volume, static config: No expiration (default)
  • Low-volume, dynamic config: Short expiration (1-5 minutes)
  • Security-related config: Shorter expiration (1-2 minutes)
  • Resource limits/quotas: Very short expiration (30-60 seconds)

Error Handling

The ConfigHelper is designed to handle errors gracefully:

  • If the configuration key doesn't exist, it returns the default value
  • If conversion fails, it returns the default value
  • If an exception occurs during retrieval, it returns the default value

This means you can safely use ConfigHelper without extensive try/catch blocks.

Complete Implementation

using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Concurrent;
using System.Threading;

namespace SmartWorkz.Shared.Core
{
    /// <summary>
    /// Provides strongly-typed access to configuration values with default fallbacks, caching, and cache expiration
    /// </summary>
    public static class ConfigHelper
    {
        // Internal cache entry class to track expiration
        private class CacheEntry
        {
            public object Value { get; set; }
            public DateTime? ExpiresAt { get; set; }
            public bool HasExpired => ExpiresAt.HasValue && DateTime.UtcNow > ExpiresAt.Value;
        }

        // Thread-safe cache for configuration values
        private static readonly ConcurrentDictionary<string, CacheEntry> _cache = new ConcurrentDictionary<string, CacheEntry>();
        
        // Lock for change token registration
        private static readonly object _registrationLock = new object();
        
        // Track if we've registered for configuration change notifications
        private static bool _changeTokenRegistered = false;

        /// <summary>
        /// Gets a typed configuration value with fallback and caching
        /// </summary>
        public static T Get<T>(IConfiguration config, string key, T defaultValue)
        {
            return GetWithExpiration<T>(config, key, defaultValue, null);
        }

        /// <summary>
        /// Gets a typed configuration value with fallback, caching, and custom expiration
        /// </summary>
        public static T GetWithExpiration<T>(IConfiguration config, string key, T defaultValue, TimeSpan? expiration)
        {
            if (config == null)
                throw new ArgumentNullException(nameof(config));

            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException(nameof(key));

            // Register for configuration changes if not already done
            RegisterForConfigChanges(config);

            // Generate a cache key that includes the type information
            string cacheKey = $"{key}:{typeof(T).FullName}";
            
            // Check cache for valid entry
            if (_cache.TryGetValue(cacheKey, out var entry) && !entry.HasExpired)
            {
                return (T)entry.Value;
            }
            
            // Not in cache or expired, get from configuration
            return GetAndCacheValue<T>(config, key, defaultValue, cacheKey, expiration);
        }

        /// <summary>
        /// Gets a value from configuration and caches it
        /// </summary>
        private static T GetAndCacheValue<T>(IConfiguration config, string key, T defaultValue, string cacheKey, TimeSpan? expiration)
        {
            string value = config[key];
            if (string.IsNullOrEmpty(value)) return defaultValue;
            
            try
            {
                Type type = typeof(T);
                T result;
                
                switch (Type.GetTypeCode(type))
                {
                    case TypeCode.String:
                        result = (T)(object)value;
                        break;
                    case TypeCode.Int32:
                        result = int.TryParse(value, out var i) ? (T)(object)i : defaultValue;
                        break;
                    case TypeCode.Boolean:
                        result = bool.TryParse(value, out var b) ? (T)(object)b : defaultValue;
                        break;
                    case TypeCode.Double:
                        result = double.TryParse(value, out var d) ? (T)(object)d : defaultValue;
                        break;
                    case TypeCode.Decimal:
                        result = decimal.TryParse(value, out var m) ? (T)(object)m : defaultValue;
                        break;
                    case TypeCode.Int64:
                        result = long.TryParse(value, out var l) ? (T)(object)l : defaultValue;
                        break;
                    case TypeCode.DateTime:
                        result = DateTime.TryParse(value, out var dt) ? (T)(object)dt : defaultValue;
                        break;
                    default:
                        if (type == typeof(Guid))
                            result = Guid.TryParse(value, out var g) ? (T)(object)g : defaultValue;
                        else if (type == typeof(TimeSpan))
                            result = TimeSpan.TryParse(value, out var ts) ? (T)(object)ts : defaultValue;
                        else
                            result = (T)Convert.ChangeType(value, type);
                        break;
                }
                
                // Calculate expiration if provided
                DateTime? expiresAt = expiration.HasValue 
                    ? DateTime.UtcNow.Add(expiration.Value) 
                    : null;
                
                // Cache the result
                _cache[cacheKey] = new CacheEntry { Value = result, ExpiresAt = expiresAt };
                
                return result;
            }
            catch
            {
                return defaultValue;
            }
        }

        /// <summary>
        /// Register for configuration change notifications
        /// </summary>
        private static void RegisterForConfigChanges(IConfiguration config)
        {
            if (_changeTokenRegistered) return;
            
            lock (_registrationLock)
            {
                if (_changeTokenRegistered) return;
                
                if (config is IConfigurationRoot configRoot)
                {
                    var token = configRoot.GetReloadToken();
                    token.RegisterChangeCallback(OnConfigChanged, configRoot);
                    _changeTokenRegistered = true;
                }
            }
        }
        
        /// <summary>
        /// Handle configuration change events
        /// </summary>
        private static void OnConfigChanged(object state)
        {
            // Clear all cache entries
            _cache.Clear();
            
            // Re-register for the next change
            if (state is IConfigurationRoot configRoot)
            {
                _changeTokenRegistered = false;
                RegisterForConfigChanges(configRoot);
            }
        }

        /// <summary>
        /// Clears the entire configuration cache
        /// </summary>
        public static void ClearCache()
        {
            _cache.Clear();
        }
        
        /// <summary>
        /// Removes cache entries for a specific configuration key
        /// </summary>
        public static void RemoveFromCache(string key)
        {
            if (string.IsNullOrEmpty(key)) return;
            
            // Remove all entries that start with this key
            foreach (var cacheKey in _cache.Keys)
            {
                if (cacheKey.StartsWith(key + ":"))
                {
                    _cache.TryRemove(cacheKey, out _);
                }
            }
        }
        
        /// <summary>
        /// Removes expired entries from the cache
        /// </summary>
        public static void CleanExpiredEntries()
        {
            foreach (var key in _cache.Keys)
            {
                if (_cache.TryGetValue(key, out var entry) && entry.HasExpired)
                {
                    _cache.TryRemove(key, out _);
                }
            }
        }
        
        /// <summary>
        /// Gets the number of items in the cache
        /// </summary>
        public static int CacheCount => _cache.Count;
    }
}

License

This component is part of the SmartWorkz.Shared library and is licensed under MIT.


Email Service Documentation

Overview

The EmailService component provides a robust solution for sending emails with attachments from .NET applications. It's designed to be configurable, simple to use, and includes support for various attachment types, CC/BCC recipients, and HTML content.

Features

  • Send emails with plain text or HTML content
  • Support for multiple attachment types (files, streams, or byte arrays)
  • CC and BCC recipient functionality
  • Both synchronous and asynchronous sending methods
  • Configuration through appsettings.json with sensible defaults
  • Comprehensive logging
  • Automatic MIME type detection for attachments
  • Strongly typed configuration with fallbacks

Installation

The EmailService is part of the SmartWorkz.Shared library. To use it in your project:

dotnet add package SmartWorkz.Shared

Configuration

Configure the email service in your appsettings.json file:

{
  "Email": {
    "SmtpServer": "smtp.office365.com",
    "SmtpPort": 587,
    "EnableSsl": true,
    "Username": "your-email@example.com",
    "Password": "your-password",
    "DefaultSender": "noreply@example.com",
    "DefaultSenderName": "System Notification",
    "UseDefaultCredentials": false,
    "Timeout": 100000
  }
}

Configuration Properties

Property Description Default
SmtpServer SMTP server address smtp.office365.com
SmtpPort SMTP server port 587
EnableSsl Whether to use SSL for connection true
Username Username for SMTP authentication null
Password Password for SMTP authentication null
DefaultSender Default sender email address noreply@example.com
DefaultSenderName Default sender display name System Notification
UseDefaultCredentials Use default credentials for authentication false
Timeout Timeout for SMTP operations (milliseconds) 100000

Registration with Dependency Injection

Add the email service to your dependency injection container:

// In Program.cs or Startup.cs
services.AddSingleton<EmailService>();

Basic Usage

Sending a Simple Email

// Using dependency injection
public class NotificationService
{
    private readonly EmailService _emailService;
    
    public NotificationService(EmailService emailService)
    {
        _emailService = emailService;
    }
    
    public void SendNotification(string recipient, string subject, string message)
    {
        _emailService.SendEmail(
            to: recipient,
            subject: subject,
            body: message,
            isHtml: true
        );
    }
}

Creating and Using Manually

// Create with configuration
var emailService = new EmailService(configuration);

// Send a simple email
emailService.SendEmail(
    to: "recipient@example.com",
    subject: "Test Email",
    body: "<p>This is a test email</p>",
    isHtml: true
);

Working with Attachments

The EmailAttachment class provides several factory methods to create attachments from different sources:

From File

var attachment = EmailAttachment.FromFile("C:\\path\\to\\document.pdf");

From Stream

using var stream = new FileStream("document.pdf", FileMode.Open);
var attachment = EmailAttachment.FromStream(stream, "document.pdf");

From Byte Array

byte[] pdfData = GetPdfData();
var attachment = EmailAttachment.FromBytes(pdfData, "report.pdf");

Sending Email with Attachments

var attachments = new List<EmailAttachment>
{
    EmailAttachment.FromFile("C:\\path\\to\\document.pdf"),
    EmailAttachment.FromBytes(pdfBytes, "report.pdf", "application/pdf")
};

await emailService.SendEmailAsync(
    to: "recipient@example.com",
    subject: "Report Attached",
    body: "<p>Please find the attached report.</p>",
    isHtml: true,
    attachments: attachments,
    cc: new List<string> { "manager@example.com" }
);

Advanced Usage

Multiple Recipients

You can specify multiple recipients by separating email addresses with commas or semicolons:

emailService.SendEmail(
    to: "recipient1@example.com; recipient2@example.com",
    subject: "Team Notification",
    body: "This is a team-wide notification"
);

CC and BCC Recipients

emailService.SendEmail(
    to: "recipient@example.com",
    subject: "Meeting Invitation",
    body: "You're invited to a meeting",
    cc: new List<string> { "manager@example.com" },
    bcc: new List<string> { "records@example.com" }
);

Overriding the Default Sender

emailService.SendEmail(
    to: "recipient@example.com",
    subject: "Custom Sender",
    body: "This is a message with a custom sender",
    from: "custom@example.com",
    fromName: "Custom Department"
);

Asynchronous Sending

await emailService.SendEmailAsync(
    to: "recipient@example.com",
    subject: "Async Email",
    body: "This email was sent asynchronously"
);

Error Handling

The send methods return a boolean indicating success or failure:

bool success = emailService.SendEmail(
    to: "recipient@example.com",
    subject: "Test Email",
    body: "This is a test email"
);

if (!success)
{
    // Handle sending failure
    logger.LogWarning("Failed to send email to recipient@example.com");
}

Best Practices

  1. Security: Store credentials securely using secrets management or environment variables.
  2. Attachments: Dispose of attachment streams properly when needed.
  3. Templating: Consider using a templating engine for creating HTML emails.
  4. Batching: For large recipient lists, consider batching emails to avoid throttling.
  5. Retry Logic: Implement retry logic for transient failures.

Troubleshooting

Common issues and solutions:

  • Authentication failures: Verify credentials and check if your email provider requires app passwords.
  • Timeout errors: Increase the timeout value in configuration for large attachments.
  • Emails marked as spam: Make sure sender domain has proper SPF, DKIM, and DMARC records.
  • Attachment issues: Check if attachment streams are positioned correctly and not disposed.

License

This component is part of the SmartWorkz.Shared library and is licensed under MIT.


EncryptionHelper Documentation

Overview

The EncryptionHelper is a robust security component that provides symmetric encryption, decryption, and secure password generation capabilities for .NET applications. It's designed to be configurable, easy to use, and follows cryptographic best practices.

Features

  • AES-256 encryption and decryption
  • Secure password generation with customizable requirements
  • Configuration through appsettings.json with sensible defaults
  • Support for fixed or random initialization vectors (IV)
  • Hash generation and verification
  • Thread-safe implementation
  • Asynchronous methods for better performance
  • Comprehensive logging

Installation

The EncryptionHelper is part of the SmartWorkz.Shared library. To use it in your project:

dotnet add package SmartWorkz.Shared

Configuration

Configure the encryption service in your appsettings.json file:

{
  "Encryption": {
    "Key": "YourSecretEncryptionKey!",
    "UseFixedIV": true,
    "KeySize": 256,
    "IV": "Base64EncodedIVString=="
  },
  "Password": {
    "Length": 8,
    "MinUppercase": 1,
    "MinLowercase": 3,
    "MinSpecial": 2,
    "MinDigits": 2,
    "UppercaseChars": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    "LowercaseChars": "abcdefghijklmnopqrstuvwxyz",
    "SpecialChars": "!@#$%^&*?/",
    "Digits": "0123456789"
  }
}

Configuration Properties

Encryption Settings
Property Description Default
Key Secret key used for encryption/decryption "KrishnayanAstro!@#AZ"
UseFixedIV Whether to use a fixed IV (less secure but compatible) true
KeySize Size of the encryption key in bits (128, 192, or 256) 256
IV Base64-encoded initialization vector null (zeros)
Password Generation Settings
Property Description Default
Length Length of generated passwords 8
MinUppercase Minimum number of uppercase characters 1
MinLowercase Minimum number of lowercase characters 3
MinSpecial Minimum number of special characters 2
MinDigits Minimum number of digits 2
UppercaseChars Characters to use for uppercase letters "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LowercaseChars Characters to use for lowercase letters "abcdefghijklmnopqrstuvwxyz"
SpecialChars Characters to use for special characters "!@#$%^&*?/"
Digits Characters to use for digits "0123456789"

Registration with Dependency Injection

Add the encryption helper to your dependency injection container:

// In Program.cs or Startup.cs
services.AddSingleton<EncryptionHelper>();

Basic Usage

Encrypting and Decrypting Data

// Using dependency injection
public class UserService
{
    private readonly EncryptionHelper _encryptionHelper;
    
    public UserService(EncryptionHelper encryptionHelper)
    {
        _encryptionHelper = encryptionHelper;
    }
    
    public string SecureUserData(string sensitiveData)
    {
        return _encryptionHelper.Encrypt(sensitiveData);
    }
    
    public string RetrieveUserData(string encryptedData)
    {
        return _encryptionHelper.Decrypt(encryptedData);
    }
}

Creating and Using Manually

// Create with configuration
var encryptionHelper = new EncryptionHelper(configuration);

// Encrypt data
string encrypted = encryptionHelper.Encrypt("Sensitive information");
Console.WriteLine($"Encrypted: {encrypted}");

// Decrypt data
string decrypted = encryptionHelper.Decrypt(encrypted);
Console.WriteLine($"Decrypted: {decrypted}");

Password Generation

Generating Passwords with Default Settings

var encryptionHelper = new EncryptionHelper(configuration);

// Generate password with configured settings
string password = encryptionHelper.GeneratePassword();
Console.WriteLine($"Generated password: {password}");

Generating Passwords with Custom Length

// Generate a 12-character password
string password = encryptionHelper.GeneratePassword(12);

Hash Generation and Verification

Creating and Verifying Hashes

// Hash a password with a user-specific salt (e.g., user ID)
string userId = "user123";
string passwordHash = encryptionHelper.GenerateHash("mySecurePassword", userId);

// Later, verify the password
bool isValid = encryptionHelper.VerifyHash("mySecurePassword", passwordHash, userId);

Advanced Usage

Asynchronous Encryption and Decryption

// Encrypt data asynchronously
string encrypted = await encryptionHelper.EncryptAsync("Sensitive information");

// Decrypt data asynchronously
string decrypted = await encryptionHelper.DecryptAsync(encrypted);

Using a Different Encryption Key

// Option 1: Configure in appsettings.json
// "Encryption": { "Key": "YourCustomKey" }

// Option 2: Create with custom settings
var options = new EncryptionOptions
{
    Key = "YourCustomKey",
    UseFixedIV = false, // More secure - uses random IV
    KeySize = 256
};

var encryptionHelper = new EncryptionHelper(options, new PasswordGenerationOptions());

Custom Password Generation

// Create custom password options
var passwordOptions = new PasswordGenerationOptions
{
    Length = 16,
    MinUppercase = 2,
    MinLowercase = 8,
    MinSpecial = 3,
    MinDigits = 3,
    SpecialChars = "@#$%&*!"
};

var encryptionHelper = new EncryptionHelper(new EncryptionOptions(), passwordOptions);
string strongPassword = encryptionHelper.GeneratePassword();

Security Considerations

Key Management

For production deployments, never hardcode encryption keys or store them in source control. Instead:

  1. Azure Key Vault: Store keys in Azure Key Vault and access them securely

    var secretClient = new SecretClient(new Uri("https://your-key-vault.vault.azure.net/"), new DefaultAzureCredential());
    KeyVaultSecret secret = await secretClient.GetSecretAsync("EncryptionKey");
    string key = secret.Value;
    
  2. Environment Variables: Use environment variables on your server

    string key = Environment.GetEnvironmentVariable("ENCRYPTION_KEY");
    
  3. User Secrets: During development, use user secrets

    {
      "Encryption": {
        "Key": "development-only-key"
      }
    }
    

Advanced Security Options

For highest security, consider these options:

  1. Disable fixed IV: Set UseFixedIV to false to use random IVs
  2. Use PBKDF2: Enable key derivation with PBKDF2 for stronger keys
    {
      "Encryption": {
        "UsePbkdf2": true,
        "Salt": "YourSaltValue",
        "Iterations": 10000
      }
    }
    

Performance Considerations

  • The EncryptionHelper caches derived keys for better performance
  • For bulk operations, reuse the same EncryptionHelper instance
  • Asynchronous methods (EncryptAsync/DecryptAsync) are recommended for I/O-bound operations
  • Consider using a memory cache for frequently encrypted/decrypted values

Best Practices

  1. Secure Configuration: Store encryption keys securely using secrets management
  2. Error Handling: Always handle encryption/decryption exceptions gracefully
  3. Key Rotation: Implement a strategy for rotating encryption keys periodically
  4. Testing: Thoroughly test encryption/decryption with various input types
  5. Logging: Enable logging but be careful not to log sensitive information

Troubleshooting

Common issues and solutions:

  • Decryption failures: Ensure you're using the same key and IV that were used for encryption
  • Invalid padding: Usually indicates the encrypted text was modified or corrupted
  • Performance issues: Make sure you're reusing the EncryptionHelper instance rather than creating new ones

Complete Implementation Example

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static void Main(string[] args)
    {
        // Setup configuration
        var configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddUserSecrets<Program>()
            .AddEnvironmentVariables()
            .Build();
            
        // Setup DI
        var services = new ServiceCollection();
        services.AddSingleton<IConfiguration>(configuration);
        services.AddSingleton<EncryptionHelper>();
        
        var serviceProvider = services.BuildServiceProvider();
        
        // Use the encryption helper
        var encryptionHelper = serviceProvider.GetRequiredService<EncryptionHelper>();
        
        // Test functionality
        TestEncryption(encryptionHelper);
        TestPasswordGeneration(encryptionHelper);
    }
    
    private static void TestEncryption(EncryptionHelper encryptionHelper)
    {
        string original = "Sensitive data to encrypt";
        string encrypted = encryptionHelper.Encrypt(original);
        string decrypted = encryptionHelper.Decrypt(encrypted);
        
        Console.WriteLine($"Original: {original}");
        Console.WriteLine($"Encrypted: {encrypted}");
        Console.WriteLine($"Decrypted: {decrypted}");
        Console.WriteLine($"Matches: {original == decrypted}");
    }
    
    private static void TestPasswordGeneration(EncryptionHelper encryptionHelper)
    {
        string password = encryptionHelper.GeneratePassword();
        Console.WriteLine($"Generated password: {password}");
        
        string password12 = encryptionHelper.GeneratePassword(12);
        Console.WriteLine($"12-char password: {password12}");
    }
}

License

This component is part of the SmartWorkz.Shared library and is licensed under MIT.


GenericApiClient

The GenericApiClient is the core class for making HTTP requests to RESTful APIs. It handles authentication, serialization, caching, and error handling.

Constructor

The GenericApiClient can be instantiated in two ways:

// Using IOptions pattern (recommended for DI)
public GenericApiClient(IOptions<ApiClientSettings> settings, IMultiLevelCache cache = null)

// Using direct settings
public GenericApiClient(ApiClientSettings settings, IMultiLevelCache cache = null)

Basic Usage

GET Requests

// Simple GET request
var weather = await _apiClient.GetAsync<WeatherForecast>("/weather/forecast");

// GET with query parameters
var searchResults = await _apiClient.GetAsync<SearchResults>(
    "/search",
    new Dictionary<string, string> { ["query"] = "test", ["page"] = "1" }
);

// GET with cancellation token
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var timeBoxedResult = await _apiClient.GetAsync<ApiResult>(
    "/endpoint", 
    cancellationToken: cts.Token
);

POST Requests

// POST with data
var newUser = new UserCreateRequest { Name = "John Doe", Email = "john@example.com" };
var result = await _apiClient.PostAsync<UserResponse>("/users", newUser);

// POST with data and query parameters
var createResult = await _apiClient.PostAsync<CreateResult>(
    "/items",
    new ItemRequest { Name = "New Item" },
    new Dictionary<string, string> { ["mode"] = "quick" }
);

PUT and DELETE Requests

// PUT request
var updateResult = await _apiClient.PutAsync<UpdateResult>(
    $"/users/{userId}",
    new UserUpdateRequest { Name = "Updated Name" }
);

// DELETE request
var deleteResult = await _apiClient.DeleteAsync<DeleteResult>($"/users/{userId}");

Advanced Features

Custom Headers

var result = await _apiClient.GetAsync<ApiResult>(
    "/special-endpoint",
    headers: new Dictionary<string, string> 
    { 
        ["X-Custom-Header"] = "CustomValue",
        ["X-Correlation-Id"] = correlationId
    }
);

Raw HTTP Methods

For more control, you can use the base SendAsync methods:

// Get raw response string
var responseString = await _apiClient.SendAsync(
    HttpMethod.Get,
    "/endpoint",
    queryParams: new Dictionary<string, string> { ["param"] = "value" }
);

// Deserialize to specific type
var result = await _apiClient.SendAsync<ApiResult>(
    HttpMethod.Post,
    "/endpoint",
    data: new { key = "value" },
    additionalHeaders: new Dictionary<string, string> { ["X-Custom"] = "Value" }
);

Performance Considerations

  • The client maintains a single HttpClient instance for connection pooling
  • Rate limiting is controlled via MaxConcurrentRequests setting
  • Response caching is handled automatically based on cache attributes
  • Compression is enabled by default to reduce bandwidth usage

Implementing in DI Container

// In Program.cs or Startup.cs
services.Configure<ApiClientSettings>(Configuration.GetSection("ApiSettings"));
services.AddSingleton<IMultiLevelCache, YourCacheImplementation>(); // Optional
services.AddSingleton<GenericApiClient>();

// In your service
public class MyService
{
    private readonly GenericApiClient _apiClient;
    
    public MyService(GenericApiClient apiClient)
    {
        _apiClient = apiClient;
    }
    
    // Use the client in your methods
}

Error Handling

The client throws exceptions for HTTP errors. Common patterns for handling:

try
{
    var result = await _apiClient.GetAsync<ApiResult>("/endpoint");
    // Process successful result
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
    // Handle 404 Not Found
}
catch (HttpRequestException ex)
{
    // Handle other HTTP errors
    logger.LogError(ex, "Error calling API");
}
catch (Exception ex)
{
    // Handle other exceptions
    logger.LogError(ex, "Unexpected error");
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

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

License

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

Support

If you encounter any issues or have questions, please file an issue on the GitHub repository.

Acknowledgments

  • Thanks to all contributors who have helped shape this library
  • Inspired by best practices in HTTP client libraries
  • Built with ❤️ by the SmartWorkz team
Product Compatible and additional computed target framework versions.
.NET 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 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 164 3/23/2025
1.0.0 75 3/22/2025