在工业4.0时代,数据库连接管理的重要性不言而喻。你是否遇到过这样的困扰:系统启动时需要连接多个数据库,但某些数据库在特定时刻才会被使用?传统的做法是在程序启动时就建立所有连接,这不仅消耗资源,还可能因为某个数据库暂时不可用而导致整个系统启动失败。
今天,我将通过一个工业级的实战案例,带你深入理解C#中的Lazy<T>
延迟加载机制,并构建一个完整的数据库连接管理系统。这套方案已在多个工业项目中验证,能有效解决连接资源浪费、启动失败等常见问题。
在工业应用中,系统通常需要连接历史数据库、配置数据库、日志数据库、设备数据库等多个数据源。传统做法是在系统初始化时就建立所有连接:
C#// ❌ 传统做法的问题
public class TraditionalDatabaseManager
{
private readonly IDbConnection _historyDb;
private readonly IDbConnection _configDb;
private readonly IDbConnection _logDb;
public TraditionalDatabaseManager()
{
// 启动时就创建所有连接,即使暂时用不到
_historyDb = new SqlConnection("..."); // 可能很耗时
_configDb = new SqlConnection("..."); // 可能失败
_logDb = new SqlConnection("..."); // 资源浪费
_historyDb.Open();
_configDb.Open();
_logDb.Open();
}
}
这种方式存在三大问题:
工业环境下,多线程并发访问数据库是常态。如何确保连接的创建既是延迟的,又是线程安全的,这是一个技术难点。
Lazy<T>
是.NET提供的延迟初始化类,它具有两个关键特性:
Value
属性时才会执行初始化逻辑LazyThreadSafetyMode
参数控制并发行为C#// ✅ Lazy延迟加载的正确姿势
public class SmartDatabaseManager
{
private readonly Lazy<IDbConnection> _historyDbLazy;
private readonly Lazy<IDbConnection> _configDbLazy;
public SmartDatabaseManager()
{
// 延迟初始化,线程安全
_historyDbLazy = new Lazy<IDbConnection>(() =>
{
Console.WriteLine("🔗 现在才创建历史数据库连接");
return CreateConnection("历史库连接字符串");
}, LazyThreadSafetyMode.ExecutionAndPublication);
_configDbLazy = new Lazy<IDbConnection>(() =>
{
Console.WriteLine("⚙️ 现在才创建配置数据库连接");
return CreateConnection("配置库连接字符串");
}, LazyThreadSafetyMode.ExecutionAndPublication);
}
// 第一次访问时才会触发连接创建
public IDbConnection HistoryDb => _historyDbLazy.Value;
public IDbConnection ConfigDb => _configDbLazy.Value;
// 检查连接是否已创建(不会触发初始化)
public bool IsHistoryDbInitialized => _historyDbLazy.IsValueCreated;
}
LazyThreadSafetyMode
有三种模式:
在工业应用中,推荐使用ExecutionAndPublication
模式。
基于延迟加载思想,我设计了一个完整的工业级数据库连接管理系统:
C#public enum DatabaseType
{
History, // 历史数据库
Config, // 配置数据库
Log, // 日志数据库
Equipment // 设备数据库
}
C#public class IndustrialDatabaseManager : IDisposable
{
private readonly ILogger<IndustrialDatabaseManager> _logger;
private readonly DatabaseConfig _config;
private readonly Dictionary<DatabaseType, Lazy<IDbConnection>> _connections;
private readonly Timer _healthCheckTimer;
private bool _disposed = false;
public IndustrialDatabaseManager(IOptions<DatabaseConfig> config, ILogger<IndustrialDatabaseManager> logger)
{
_config = config.Value;
_logger = logger;
_connections = new Dictionary<DatabaseType, Lazy<IDbConnection>>();
InitializeConnections();
// 启动健康检查定时器(每30秒检查一次)
_healthCheckTimer = new Timer(PerformHealthCheck, null,
TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30));
_logger.LogInformation("🏭 工业数据库管理器初始化完成");
}
private void InitializeConnections()
{
// 历史数据库 - 延迟加载
_connections[DatabaseType.History] = new Lazy<IDbConnection>(() =>
{
_logger.LogInformation("🔗 正在初始化历史数据库连接...");
return CreateSqliteConnection(_config.HistoryDbConnectionString, DatabaseType.History);
}, LazyThreadSafetyMode.ExecutionAndPublication);
// 配置数据库 - 延迟加载
_connections[DatabaseType.Config] = new Lazy<IDbConnection>(() =>
{
_logger.LogInformation("⚙️ 正在初始化配置数据库连接...");
return CreateSqliteConnection(_config.ConfigDbConnectionString, DatabaseType.Config);
}, LazyThreadSafetyMode.ExecutionAndPublication);
// 设备数据库 - 延迟加载
_connections[DatabaseType.Equipment] = new Lazy<IDbConnection>(() =>
{
_logger.LogInformation("🏭 正在初始化设备数据库连接...");
return CreateSqliteConnection(_config.EquipmentDbConnectionString, DatabaseType.Equipment);
}, LazyThreadSafetyMode.ExecutionAndPublication);
}
// 优雅的属性访问方式
public IDbConnection HistoryDb => GetConnection(DatabaseType.History);
public IDbConnection ConfigDb => GetConnection(DatabaseType.Config);
public IDbConnection EquipmentDb => GetConnection(DatabaseType.Equipment);
// 检查连接状态但不触发初始化
public bool IsConnectionInitialized(DatabaseType dbType)
{
return _connections.ContainsKey(dbType) && _connections[dbType].IsValueCreated;
}
}
C#private void PerformHealthCheck(object state)
{
if (_disposed) return;
foreach (var kvp in _connections)
{
if (kvp.Value.IsValueCreated)
{
try
{
var connection = kvp.Value.Value;
if (connection.State != ConnectionState.Open)
{
_logger.LogWarning("🔄 检测到 {DbType} 连接断开,尝试重连...", kvp.Key);
connection.Open();
_logger.LogInformation("✅ {DbType} 连接重连成功", kvp.Key);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "❌ {DbType} 连接健康检查失败", kvp.Key);
}
}
}
}
C#using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace AppLazySqllite
{
internal class Program
{
static async Task Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
// 创建主机构建器
var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureServices((context, services) =>
{
// 添加工业数据库管理器
services.AddIndustrialDatabaseManager(context.Configuration);
});
// 构建主机
using var host = builder.Build();
// 获取服务
var logger = host.Services.GetRequiredService<ILogger<Program>>();
var dbManager = host.Services.GetRequiredService<DatabaseManager>();
var equipmentService = host.Services.GetRequiredService<EquipmentDataService>();
logger.LogInformation("工业数据库管理系统启动");
try
{
// 示例:保存设备数据
var equipmentData = new EquipmentData
{
DeviceId = "PUMP_001",
DeviceName = "主泵1号",
Status = EquipmentStatus.Running,
Temperature = 45.5,
Pressure = 2.3,
Speed = 1500.0,
IsOnline = true
};
var saved = await equipmentService.SaveEquipmentDataAsync(equipmentData);
logger.LogInformation("设备数据保存结果: {Result}", saved ? "成功" : "失败");
// 示例:查询在线设备
var onlineEquipment = await equipmentService.GetOnlineEquipmentAsync();
logger.LogInformation("在线设备数量: {Count}", onlineEquipment.Count());
// 显示连接状态
var connectionStatus = dbManager.GetConnectionStatus();
foreach (var status in connectionStatus)
{
logger.LogInformation("数据库 {DbType} 连接状态: {Status}",
status.Key, status.Value ? "已初始化" : "未初始化");
}
logger.LogInformation("✅ 示例程序执行完成");
}
catch (Exception ex)
{
logger.LogError(ex, "❌ 程序执行出错");
}
finally
{
// 释放资源
dbManager.Dispose();
}
}
}
}
坑点1:忘记处理初始化异常
C#// ❌ 错误做法
public IDbConnection HistoryDb => _historyDbLazy.Value; // 如果初始化失败,每次访问都会重新抛异常
// ✅ 正确做法
public IDbConnection HistoryDb
{
get
{
try
{
return _historyDbLazy.Value;
}
catch (Exception ex)
{
_logger.LogError(ex, "历史数据库连接创建失败");
throw;
}
}
}
坑点2:资源释放不当
C#// ✅ 正确的资源释放
public void Dispose()
{
if (_disposed) return;
_healthCheckTimer?.Dispose();
// 只释放已创建的连接
foreach (var connection in _connections.Values)
{
if (connection.IsValueCreated)
{
connection.Value?.Dispose();
}
}
_disposed = true;
}
JSON{
"DatabaseConfig": {
"HistoryDbConnectionString": "Data Source=./data/history.db;Cache=Shared",
"ConfigDbConnectionString": "Data Source=./data/config.db;Cache=Shared",
"EquipmentDbConnectionString": "Data Source=./data/equipment.db;Cache=Shared",
"ConnectionTimeout": 30,
"EnableConnectionPooling": true
}
}
C#public static class ServiceCollectionExtensions
{
public static IServiceCollection AddIndustrialDatabaseManager(
this IServiceCollection services,
IConfiguration configuration)
{
// 注册配置
services.Configure<DatabaseConfig>(
configuration.GetSection("DatabaseConfig"));
// 注册数据库管理器为单例
services.AddSingleton<IndustrialDatabaseManager>();
return services;
}
}
通过实际测试,采用Lazy延迟加载方案后:
ExecutionAndPublication
IDisposable
模式确保资源正确释放通过本文的深入讲解,我们学会了如何使用Lazy<T>
构建一个工业级的数据库连接管理系统。核心要点回顾:
Lazy<T>
让我们实现按需创建连接,既节省资源又提高启动速度ExecutionAndPublication
模式确保多线程环境下的安全性这套方案不仅适用于工业4.0场景,同样可以应用于微服务架构、物联网平台、企业级应用等需要管理多数据源的场景中。
你在项目中是如何处理多数据库连接管理的?是否也遇到过启动慢、资源浪费的问题? 欢迎在评论区分享你的经验和遇到的挑战,让我们一起讨论更优的解决方案。
觉得这篇文章对你有帮助?请转发给更多需要的C#开发同行!关注我,获取更多实战级C#开发技巧。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!