C#:如何为IDbConnection添加标识判断连接字符串是否已修改?
解决方案:避免连接字符串自定义关键字,给IDbConnection添加自定义标识
你遇到的问题本质是:数据库连接字符串的关键字由对应的数据库驱动(如SqlClient、OracleClient)严格校验,自定义的authenticated不在驱动支持的关键字列表中,因此赋值时会抛出异常。以下两种方案可以实现你的需求,且不会修改连接字符串本身:
方案1:使用扩展方法+ConditionalWeakTable存储自定义属性
这种方法不需要修改原有连接对象的结构,通过扩展方法为IDbConnection附加自定义状态,且不会导致内存泄漏(ConditionalWeakTable会在连接对象被回收时自动清理对应的元数据)。
扩展类代码
using System.Runtime.CompilerServices; public static class DbConnectionExtensions { // 用于存储每个连接的元数据 private static readonly ConditionalWeakTable<IDbConnection, ConnectionMetadata> _connectionMetadata = new(); // 内部类存储自定义属性 private class ConnectionMetadata { public bool IsAuthenticated { get; set; } } // 设置已认证标记 public static void MarkAsAuthenticated(this IDbConnection connection) { var metadata = _connectionMetadata.GetValue(connection, _ => new ConnectionMetadata()); metadata.IsAuthenticated = true; } // 判断是否已认证 public static bool IsAuthenticated(this IDbConnection connection) { return _connectionMetadata.TryGetValue(connection, out var metadata) && metadata.IsAuthenticated; } }
修改你的方法并使用
// 修改原方法,不再添加自定义连接字符串关键字 public void SetUserAndPassword(IDbConnection dbConnection, string user, string password) { DbConnectionStringBuilder sb = new DbConnectionStringBuilder(); sb.ConnectionString = dbConnection.ConnectionString; sb["user id"] = user; sb["password"] = password; dbConnection.ConnectionString = sb.ConnectionString; // 标记连接已更新 dbConnection.MarkAsAuthenticated(); } // 使用时判断是否需要更新 if (!dbConnection.IsAuthenticated()) { vendor.SetUserAndPassword(dbConnection, dbCredentials.GetUser(), dbCredentials.GetPassword()); } // 执行打开连接等操作
方案2:包装IDbConnection实现自定义属性
如果需要更明确的封装,可以自己实现一个IDbConnection的包装类,内部委托原始连接的所有操作,同时添加自定义属性。
包装类代码
public class TrackedDbConnection : IDbConnection { private readonly IDbConnection _innerConnection; // 自定义属性:标记是否已更新用户名密码 public bool IsAuthenticated { get; set; } public TrackedDbConnection(IDbConnection innerConnection) { _innerConnection = innerConnection ?? throw new ArgumentNullException(nameof(innerConnection)); } // 实现IDbConnection的所有接口方法,委托给内部连接 public string ConnectionString { get => _innerConnection.ConnectionString; set => _innerConnection.ConnectionString = value; } public int ConnectionTimeout => _innerConnection.ConnectionTimeout; public string Database => _innerConnection.Database; public ConnectionState State => _innerConnection.State; public IDbTransaction BeginTransaction() => _innerConnection.BeginTransaction(); public IDbTransaction BeginTransaction(IsolationLevel il) => _innerConnection.BeginTransaction(il); public void ChangeDatabase(string databaseName) => _innerConnection.ChangeDatabase(databaseName); public void Close() => _innerConnection.Close(); public IDbCommand CreateCommand() => _innerConnection.CreateCommand(); public void Open() => _innerConnection.Open(); public void Dispose() => _innerConnection.Dispose(); }
使用示例
// 包装原始连接 var trackedConn = new TrackedDbConnection(dbConnection); if (!trackedConn.IsAuthenticated) { vendor.SetUserAndPassword(trackedConn, dbCredentials.GetUser(), dbCredentials.GetPassword()); trackedConn.IsAuthenticated = true; } // 使用trackedConn进行后续的数据库操作 trackedConn.Open(); // ...
为什么原方法会报错
数据库驱动(如SqlConnection对应的SqlClient)在解析连接字符串时,会严格校验每个关键字是否属于其支持的列表(比如user id、password、server等),自定义的authenticated不在列表中,因此会抛出ArgumentException。
内容的提问来源于stack exchange,提问作者Bar
相关产品推荐
相关产品推荐

