优化SqlDataReader的SafeGetInt扩展方法:解决频繁抛异常的性能问题
优化SqlDataReader安全读取扩展方法的方案
原实现依赖try-catch处理列不存在或空值的情况,在高频调用场景下频繁抛出异常会带来显著的性能损耗。以下是更高效的优化方案,核心思路是预先缓存列名与索引的映射,避免异常抛出并减少重复计算:
1. 封装列索引缓存逻辑
首先创建辅助方法生成列名到索引的映射,并通过ConditionalWeakTable缓存每个SqlDataReader的映射表(既避免内存泄漏,又保证同一reader仅生成一次映射):
using System.Collections.Generic; using System.Data.SqlClient; using System.Runtime.CompilerServices; using System; public static class SqlDataReaderExtensions { // 缓存每个SqlDataReader的列名-索引映射,自动随reader回收释放 private static readonly ConditionalWeakTable<SqlDataReader, Dictionary<string, int>> _columnIndexMaps = new ConditionalWeakTable<SqlDataReader, Dictionary<string, int>>(); // 生成当前reader的列名-索引映射(不区分大小写,适配SQL列名特性) private static Dictionary<string, int> GetColumnIndexMap(this SqlDataReader reader) { var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < reader.FieldCount; i++) { map.Add(reader.GetName(i), i); } return map; }
2. 各类型安全读取方法
基于缓存的映射表,实现各类型的安全读取方法,完全避免异常抛出:
Int类型
public static int SafeGetInt(this SqlDataReader reader, string field) { var columnMap = _columnIndexMaps.GetValue(reader, r => r.GetColumnIndexMap()); if (columnMap.TryGetValue(field, out int colIndex) && !reader.IsDBNull(colIndex)) { return reader.GetInt32(colIndex); } return 0; // 可根据业务需求修改默认值 }
String类型
public static string SafeGetString(this SqlDataReader reader, string field) { var columnMap = _columnIndexMaps.GetValue(reader, r => r.GetColumnIndexMap()); if (columnMap.TryGetValue(field, out int colIndex) && !reader.IsDBNull(colIndex)) { return reader.GetString(colIndex); } return string.Empty; // 或返回null,根据需求调整 }
Double类型
public static double SafeGetDouble(this SqlDataReader reader, string field) { var columnMap = _columnIndexMaps.GetValue(reader, r => r.GetColumnIndexMap()); if (columnMap.TryGetValue(field, out int colIndex) && !reader.IsDBNull(colIndex)) { return reader.GetDouble(colIndex); } return 0.0; }
Boolean类型
public static bool SafeGetBool(this SqlDataReader reader, string field) { var columnMap = _columnIndexMaps.GetValue(reader, r => r.GetColumnIndexMap()); if (columnMap.TryGetValue(field, out int colIndex) && !reader.IsDBNull(colIndex)) { return reader.GetBoolean(colIndex); } return false; } }
3. 性能优化说明
- 消除异常开销:通过
TryGetValue判断列是否存在,完全替代原方案中依赖catch处理异常的逻辑,彻底消除异常抛出和捕获的性能损耗。 - 减少重复计算:每个
SqlDataReader的列映射仅在首次调用时生成,后续复用缓存结果,避免了重复调用GetOrdinal遍历列名的开销。 - 内存安全:使用
ConditionalWeakTable存储映射表,不会阻止SqlDataReader对象被垃圾回收,无内存泄漏风险。
使用方式与原代码完全一致:
myObject.myProperty = reader.SafeGetInt("MyColumn");
内容的提问来源于stack exchange,提问作者Smith5727
相关产品推荐
相关产品推荐

