OElite.Common.Hosting 5.0.9-develop.462
OElite.Common.Hosting
Generic application hosting and lifecycle management for OElite applications.
Overview
OElite.Common.Hosting provides streamlined application startup and lifecycle management for OElite applications. It eliminates the need for manual service registration, configuration setup, and middleware pipeline configuration by providing automatic discovery and configuration based on application type detection.
This package provides the generic hosting foundation used by all OElite applications, including console apps, background services, and web applications. For web-specific features, use OElite.Common.Hosting.AspNetCore.
Features
🚀 Automatic Service Discovery
- Service Registration: Auto-discovery of
IOEliteServiceimplementations - Repository Registration: Auto-discovery of
IOEliteDataRepository<>implementations - Options Registration: Auto-discovery of
IOEliteOptionsimplementations - Bootstrap Providers: Auto-discovery of
IBootstrapProviderimplementations
🔧 Intelligent Configuration Management
- App Type Detection: Automatic detection of OElite app types (Platform, Kortex, Obelisk, etc.)
- Configuration Composition: Merges local and Kortex remote configurations
- DbCentre Resolution: Automatic database center selection based on app configuration
- Path Resolution: Standardized path management across all app types
🔄 Advanced Lifecycle Management
- Bootstrap Orchestration: Ordered execution of initialization providers
- Background Services: Automatic hosted service registration
- Model Transformation: Auto-discovery and registration of model transformers
- Graceful Shutdown: Proper resource disposal and cleanup
💾 Integrated Caching Strategy
- Redis Cache: Distributed caching using OElite.Restme.Redis
- Memory Cache: In-memory caching with OElite.Restme.Memory
- Fallback Logic: Automatic fallback to memory cache when Redis unavailable
Quick Start
1. Installation
dotnet add package OElite.Common.Hosting
2. Basic Console Application
using OElite.Common.Hosting.Bootstrap;
using OElite.Common.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public class MyAppConfig : BaseAppConfig
{
public override string? DbCentreFullClassName =>
"MyApp.Data.MyDbCentre, MyApp.Data";
public MyAppConfig(string jsonConfig, ILogger? logger = null)
: base(OeAppType.GeneralConsoleApp, jsonConfig, logger)
{
}
}
public class Program
{
public static async Task Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false)
.AddEnvironmentVariables()
.Build();
var jsonConfig = File.ReadAllText("appsettings.json");
var appConfig = new MyAppConfig(jsonConfig);
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
// Add OElite hosting with automatic service discovery
services.AddOeApp(configuration, appConfig, options =>
{
// Optional: Configure service discovery
options.IncludeAssemblyPrefixes.Add("MyApp");
options.ExcludeNamespacePrefixes.Add("MyApp.Legacy");
});
})
.Build();
// Execute bootstrap providers
await host.Services.ExecuteBootstrapProviderAsync();
await host.RunAsync();
}
}
3. Background Service Application
using OElite.Common.Hosting.Lifecycle;
public class DataSyncBackgroundService : OEliteBackgroundService
{
private readonly ILogger<DataSyncBackgroundService> _logger;
private readonly IMyDataService _dataService;
public DataSyncBackgroundService(
ILogger<DataSyncBackgroundService> logger,
IMyDataService dataService)
{
_logger = logger;
_dataService = dataService;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await _dataService.SynchronizeDataAsync();
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Data synchronization failed");
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}
}
// The service will be automatically discovered and registered as a hosted service
Advanced Configuration
Service Discovery Options
services.AddOeApp(configuration, appConfig, options =>
{
// Assembly filtering
options.IncludeAssemblyPrefixes.Add("MyApp");
options.IncludeAssemblyPrefixes.Add("MyCompany.Shared");
options.ExcludeAssemblyPrefixes.Add("MyApp.ThirdParty");
// Namespace filtering
options.IncludeNamespacePrefixes.Add("MyApp.Services");
options.IncludeNamespacePrefixes.Add("MyApp.Repositories");
options.ExcludeNamespacePrefixes.Add("MyApp.Legacy");
// Specific type exclusions
options.ExcludeServiceTypes.Add(typeof(LegacyService));
options.ExcludeRepositoryTypes.Add(typeof(DeprecatedRepository));
// Custom type filters
options.ServiceTypeFilter = type =>
!type.Name.Contains("Test") && !type.Name.Contains("Mock");
options.RepositoryTypeFilter = type =>
type.Namespace?.Contains("Production") == true;
});
Bootstrap Provider Implementation
using OElite.Common.Hosting.Bootstrap;
public class DatabaseMigrationBootstrap : IBootstrapProvider
{
public string Name => "Database Migration";
public int Priority => 10; // Lower numbers run first
private readonly ILogger<DatabaseMigrationBootstrap> _logger;
private readonly IMyDbService _dbService;
public DatabaseMigrationBootstrap(
ILogger<DatabaseMigrationBootstrap> logger,
IMyDbService dbService)
{
_logger = logger;
_dbService = dbService;
}
public async Task<bool> InitializeAsync()
{
try
{
_logger.LogInformation("Running database migrations...");
await _dbService.MigrateAsync();
_logger.LogInformation("Database migrations completed successfully");
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Database migration failed");
return false; // Return false to indicate failure
}
}
}
// This will be automatically discovered and executed during startup
Model Transformer Registration
using OElite.Common.ModelTransformation;
public class ProductApiTransformer : IModelTransformer
{
public Type EntityType => typeof(Product);
public Type CollectionType => typeof(List<Product>);
public int Priority => 10;
public bool CanHandle(TransformationContext context)
{
return context.TargetFormat == "api";
}
public object? TransformObject(object obj, TransformationContext context)
{
if (obj is Product product)
{
return new ProductApiModel
{
Id = product.Id,
Name = product.Name,
Price = product.Price
// Transform to API format
};
}
return null;
}
// Implement remaining interface members...
}
// The transformer will be automatically discovered and registered
Application Types
OElite.Common.Hosting supports all standard OElite application types:
public enum OeAppType
{
Platform, // Platform core services
Kortex, // Kortex proxy and management services
Obelisk, // Email server applications
OeSterling, // OeSterling business services
GeneralWebApp, // Web applications (use with AspNetCore)
GeneralWebApi, // API applications (use with AspNetCore)
GeneralConsoleApp, // Console applications
GeneralMobileApp // Mobile application backends
}
Configuration Structure by App Type
Each app type gets automatic configuration path resolution:
{
"oelite": {
"data": {
"mongodb": {
"platform": "mongodb://localhost:27017/platform",
"kortex": "mongodb://localhost:27017/kortex",
"obelisk": "mongodb://localhost:27017/obelisk",
"app": "mongodb://localhost:27017/myapp"
},
"redis": {
"platform": "redis://localhost:6379/0",
"kortex": "redis://localhost:6379/1",
"obelisk": "redis://localhost:6379/2",
"app": "redis://localhost:6379/3"
}
}
}
}
Bootstrap System
The bootstrap system executes initialization providers in priority order during application startup.
Bootstrap Provider Lifecycle
- Discovery: All
IBootstrapProviderimplementations are auto-discovered - Ordering: Providers are sorted by priority (lower numbers first)
- Execution: Each provider's
InitializeAsync()method is called - Error Handling: Failed providers are logged but don't stop the process
Built-in Bootstrap Providers
- DbBootstrapService: Initializes MongoDB collections and indexes
- TransformerAutoRegistrationService: Registers discovered model transformers
- KortexConfigurationService: Loads remote configuration from Kortex
Getting Bootstrap Information
// Get information about registered bootstrap providers
var bootstrapInfo = serviceProvider.GetBootstrapProviders();
foreach (var provider in bootstrapInfo)
{
Console.WriteLine($"Provider: {provider.Name}, Priority: {provider.Priority}");
}
Path Resolution System
Built-in Path Resolver
using OElite.Common.Hosting.Infrastructure;
// The default path resolver is automatically registered
public class MyService
{
private readonly IOElitePathResolver _pathResolver;
public MyService(IOElitePathResolver pathResolver)
{
_pathResolver = pathResolver;
}
public void DoWork()
{
// Get resolved paths
var dataPath = _pathResolver.ResolvePath(OElitePathType.Data, "products.json");
var logPath = _pathResolver.ResolvePath(OElitePathType.Logs, "api.log");
// Ensure directories exist
var cacheDir = _pathResolver.EnsureDirectory(OElitePathType.Cache, "images");
// Check file existence
if (_pathResolver.FileExists(OElitePathType.Config, "settings.json"))
{
// Process file
}
}
}
Custom Path Resolver
public class CustomPathResolver : IOElitePathResolver
{
private readonly string _baseDirectory;
public CustomPathResolver(string baseDirectory)
{
_baseDirectory = baseDirectory;
}
public string ResolvePath(OElitePathType pathType, string relativePath = "")
{
var subDir = pathType switch
{
OElitePathType.Data => "data",
OElitePathType.Logs => "logs",
OElitePathType.Cache => "cache",
OElitePathType.Temp => "temp",
OElitePathType.Config => "config",
_ => "misc"
};
return Path.Combine(_baseDirectory, subDir, relativePath);
}
public string EnsureDirectory(OElitePathType pathType, string relativePath = "")
{
var fullPath = ResolvePath(pathType, relativePath);
Directory.CreateDirectory(fullPath);
return fullPath;
}
public bool FileExists(OElitePathType pathType, string relativePath)
{
return File.Exists(ResolvePath(pathType, relativePath));
}
}
// Register custom path resolver
services.AddSingleton<IOElitePathResolver>(new CustomPathResolver("/app/data"));
Caching Integration
Automatic Cache Configuration
// Redis cache is automatically configured if connection string is available
var appConfig = new MyAppConfig(jsonConfig);
// If Redis is configured, services get:
// - IMemoryCache -> OElite.Restme memory cache
// - IDistributedCache -> OElite.Restme Redis cache
// If Redis is not configured, services get:
// - IMemoryCache -> OElite.Restme memory cache
// - IDistributedCache -> OElite.Restme memory cache (fallback)
Using Caches in Services
public class ProductService : IOEliteService
{
private readonly IDistributedCache _distributedCache;
private readonly IMemoryCache _memoryCache;
public ProductService(IDistributedCache distributedCache, IMemoryCache memoryCache)
{
_distributedCache = distributedCache;
_memoryCache = memoryCache;
}
public async Task<Product?> GetProductAsync(string productId)
{
// Try memory cache first (fastest)
if (_memoryCache.TryGetValue($"product:{productId}", out Product? cached))
{
return cached;
}
// Try distributed cache (Redis)
var distributedValue = await _distributedCache.GetStringAsync($"product:{productId}");
if (distributedValue != null)
{
var product = JsonSerializer.Deserialize<Product>(distributedValue);
// Store in memory cache for next time
_memoryCache.Set($"product:{productId}", product, TimeSpan.FromMinutes(5));
return product;
}
// Load from database and cache
var dbProduct = await LoadFromDatabaseAsync(productId);
if (dbProduct != null)
{
_memoryCache.Set($"product:{productId}", dbProduct, TimeSpan.FromMinutes(5));
await _distributedCache.SetStringAsync($"product:{productId}",
JsonSerializer.Serialize(dbProduct),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
});
}
return dbProduct;
}
}
Service and Repository Patterns
Service Implementation
public interface IProductService : IOEliteService
{
Task<Product?> GetProductAsync(string productId);
Task<List<Product>> GetProductsAsync(int skip, int take);
}
public class ProductService : IProductService
{
private readonly IProductRepository _repository;
private readonly ILogger<ProductService> _logger;
public ProductService(IProductRepository repository, ILogger<ProductService> logger)
{
_repository = repository;
_logger = logger;
}
public async Task<Product?> GetProductAsync(string productId)
{
_logger.LogDebug("Getting product {ProductId}", productId);
return await _repository.GetByIdAsync(productId);
}
public async Task<List<Product>> GetProductsAsync(int skip, int take)
{
_logger.LogDebug("Getting products: skip={Skip}, take={Take}", skip, take);
return await _repository.GetRangeAsync(skip, take);
}
}
// Automatically registered as singleton with IProductService interface
Repository Implementation
public interface IProductRepository : IOEliteDataRepository<Product>
{
Task<List<Product>> GetByCategoryAsync(string categoryId);
}
public class ProductRepository : IProductRepository
{
private readonly DbCentre _dbCentre;
public ProductRepository(DbCentre dbCentre)
{
_dbCentre = dbCentre;
}
public async Task<Product?> GetByIdAsync(string id)
{
return await _dbCentre.Products.GetByIdAsync(id);
}
public async Task<List<Product>> GetByCategoryAsync(string categoryId)
{
return await _dbCentre.Products.GetManyAsync(p => p.CategoryId == categoryId);
}
// Other IOEliteDataRepository<Product> implementations...
}
// Automatically registered as singleton with IProductRepository interface
Options Pattern Implementation
public class EmailOptions : IOEliteOptions
{
public string SmtpServer { get; set; } = "";
public int SmtpPort { get; set; } = 587;
public string Username { get; set; } = "";
public string Password { get; set; } = "";
public bool EnableSsl { get; set; } = true;
}
// Automatically registered and can be injected as EmailOptions
public class EmailService : IOEliteService
{
private readonly EmailOptions _options;
public EmailService(EmailOptions options)
{
_options = options;
}
}
DbCentre Integration
Custom DbCentre Implementation
// In your data project
public class MyAppDbCentre : DbCentre
{
public IMongoDbCollection<Product> Products { get; }
public IMongoDbCollection<Customer> Customers { get; }
public MyAppDbCentre(IAppConfig appConfig) : base(appConfig)
{
Products = GetCollection<Product>();
Customers = GetCollection<Customer>();
}
}
// In your app config
public class MyAppConfig : BaseAppConfig
{
public override string? DbCentreFullClassName =>
"MyApp.Data.MyAppDbCentre, MyApp.Data";
public MyAppConfig(string jsonConfig)
: base(OeAppType.GeneralWebApp, jsonConfig)
{
}
}
Using DbCentre in Services
public class ProductService : IOEliteService
{
private readonly MyAppDbCentre _dbCentre;
public ProductService(MyAppDbCentre dbCentre)
{
_dbCentre = dbCentre; // Specific typed DbCentre
}
public async Task<Product?> GetProductAsync(string productId)
{
return await _dbCentre.Products.GetByIdAsync(productId);
}
}
// Or use the base DbCentre if you don't need typed collections
public class GenericService : IOEliteService
{
private readonly DbCentre _dbCentre;
public GenericService(DbCentre dbCentre)
{
_dbCentre = dbCentre; // Base DbCentre
}
}
Performance Optimizations
Assembly Loading Strategy
- Loads only OElite.* assemblies to reduce discovery time
- Uses cached assembly lists to avoid repeated file system access
- Handles ReflectionTypeLoadException gracefully
Service Registration Efficiency
- Services are registered as singletons by default for best performance
- Interface registration is optimized to avoid duplicate registrations
- Hosted services use proper ASP.NET Core registration patterns
Memory Management
- Shared Redis connections prevent connection pool exhaustion
- Bootstrap providers are disposed properly after initialization
- Type discovery results are not cached to allow runtime assembly loading
Troubleshooting
Common Issues
Services Not Being Discovered
// Check assembly and namespace filters
services.AddOeApp(configuration, appConfig, options =>
{
// Make sure your assembly is included
options.IncludeAssemblyPrefixes.Add("YourApp");
// Check for exclusion filters
options.ExcludeNamespacePrefixes.Clear(); // Remove if blocking your services
});
Bootstrap Provider Not Running
// Ensure you call ExecuteBootstrapProviderAsync after host building
var host = hostBuilder.Build();
await host.Services.ExecuteBootstrapProviderAsync(); // Required!
await host.RunAsync();
DbCentre Not Resolving
public override string? DbCentreFullClassName =>
"Correct.Namespace.YourDbCentre, Correct.AssemblyName";
// Verify the type can be loaded
var type = Type.GetType("Correct.Namespace.YourDbCentre, Correct.AssemblyName");
if (type == null)
{
// Fix the type name or assembly reference
}
Cache Configuration Issues
// Check Redis connection string format
"oelite:data:redis:app": "localhost:6379" // ✅ Correct
"oelite:data:redis:app": "redis://localhost:6379" // ✅ Correct
"oelite:data:redis:app": "redis://user:pass@host:6379/0" // ✅ Correct
"oelite:data:redis": "localhost:6379" // ❌ Wrong path
Integration Examples
Console Application with Background Services
class Program
{
static async Task Main(string[] args)
{
var jsonConfig = File.ReadAllText("appsettings.json");
var appConfig = new MyAppConfig(jsonConfig);
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddOeApp(
new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build(),
appConfig);
})
.Build();
await host.Services.ExecuteBootstrapProviderAsync();
await host.RunAsync();
}
}
Unit Testing with OElite.Common.Hosting
public class ServiceTests
{
[Test]
public async Task TestServiceDiscovery()
{
var services = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new[]
{
new KeyValuePair<string, string>("oelite:data:mongodb:app", "mongodb://localhost:27017/test")
})
.Build();
var appConfig = new TestAppConfig(configuration);
services.AddOeApp(configuration, appConfig, options =>
{
// Only include test assemblies
options.IncludeAssemblyPrefixes.Clear();
options.IncludeAssemblyPrefixes.Add("MyApp.Tests");
});
var provider = services.BuildServiceProvider();
// Verify services are registered
var productService = provider.GetService<IProductService>();
Assert.IsNotNull(productService);
}
}
Version History
- 5.0.9: Current version with .NET 10.0 support
- Enhanced service discovery with filtering options
- Improved bootstrap provider orchestration
- Added comprehensive caching integration
- Optimized assembly loading and type discovery
Related Packages
- OElite.Common: Core shared components and infrastructure
- OElite.Common.Hosting.AspNetCore: Web application hosting extensions
- OElite.Services: Business logic services that integrate with this hosting
- OElite.Data: Data access components with DbCentre patterns
- OElite.Restme.Hosting: REST client and caching integration
License
Copyright © OElite Limited. All rights reserved.
Support
For support and documentation, visit https://www.oelite.com
Showing the top 20 packages that depend on OElite.Common.Hosting.
| Packages | Downloads |
|---|---|
|
OElite.Common.Hosting.AspNetCore
Package Description
|
96 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
32 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
16 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
14 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
13 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
11 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
8 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
7 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
6 |
|
OElite.Common.Hosting.AspNetCore
Package Description
|
5 |
.NET 10.0
- OElite.Common (>= 5.0.9-develop.462)
- OElite.Data (>= 5.0.9-develop.462)
- OElite.Services (>= 5.0.9-develop.462)
- Microsoft.Extensions.Configuration (>= 9.0.9)
- Microsoft.Extensions.Configuration.EnvironmentVariables (>= 9.0.9)
- Microsoft.Extensions.Configuration.FileExtensions (>= 9.0.9)
- Microsoft.Extensions.Configuration.Json (>= 9.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.9)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.9)
- Microsoft.Extensions.Logging (>= 9.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.9)
- Microsoft.Extensions.Logging.Console (>= 9.0.9)
- OElite.Restme.Hosting (>= 2.0.9-develop.442)
- OElite.Restme.MongoDb (>= 2.0.9-develop.442)