ASP.NET MVC应用中XML异常无法捕获且代码重复执行问题求助
ASP.NET MVC中Settings单例加载错误XML时无限循环且异常捕获失效问题
问题现象
为ASP.NET MVC应用编写的设置处理单例类,在测试格式错误/缺失的XML文件时,出现以下问题:
- 抛出的
XmlException看似无法被捕获,代码无限重复执行同一代码段 - 异常发生前后的断点被反复触发,异常抛出后的断点无法命中
- 已禁用Visual Studio调试对话框弹窗,且无手动循环调用属性的逻辑,异常后的默认处理逻辑未生效


原代码
设置容器属性
private static Settings _singletonSettings; public static Settings SettingsContainer { get { if (_singletonSettings == null) { _singletonSettings = new Settings(); _singletonSettings.LoadSettings(); } return _singletonSettings; } private set { _singletonSettings = value; } }
LoadSettings函数
public void LoadSettings() { string filePath = "Config/Settings.txt"; if (!Directory.Exists(Path.GetDirectoryName(filePath))) { Directory.CreateDirectory(Path.GetDirectoryName(filePath)); } if (File.Exists(filePath)) { try { SettingsContainer = LoadViaDataContractSerialization<Settings>(filePath); // 尝试捕获异常 } catch (Exception ex) { Log.GlobalLog.WriteError("LoadViaDataContractSerialization() error:\n" + ex.Message + "\nStackTrace: \n" + ex.StackTrace); } } else { File.Create(filePath); } if (SettingsContainer == null) { SettingsContainer = new Settings(); } }
LoadViaDataContractSerialization函数
public static T LoadViaDataContractSerialization<T>(string filepath) { try { T serializableObject; using (var fileStream = new FileStream(filepath, FileMode.Open)) { try { using (var reader = XmlDictionaryReader.CreateTextReader(fileStream, new XmlDictionaryReaderQuotas())) // 所有执行在此处停止 { var serializer = new DataContractSerializer(typeof(T)); serializableObject = (T)serializer.ReadObject(reader, true); reader.Close(); } } catch (XmlException ex) { Log.GlobalLog.WriteError("LoadViaDataContractSerialization() XML Fail: Message: " + ex.Message + "\n StackTrace: " + ex.StackTrace); return default(T); } fileStream.Close(); } return serializableObject; } catch (Exception ex) { Log.GlobalLog.WriteError("LoadViaDataContractSerialization() Fail: Message: " + ex.Message + "\n StackTrace: " + ex.StackTrace); return default(T); } }
问题根因
- 无限循环触发逻辑:当XML反序列化失败,
LoadViaDataContractSerialization返回default(T)(即null),执行SettingsContainer = null后,LoadSettings中的if (SettingsContainer == null)会访问SettingsContainer的getter。此时_singletonSettings已被设为null,getter会重新创建Settings实例并调用LoadSettings,形成无限循环。 - 异常看似未捕获:实际异常已被
LoadViaDataContractSerialization的catch块捕获,但循环反复触发异常抛出逻辑,导致视觉上异常一直在重复出现。
修复方案
核心修改:避免触发属性getter的循环初始化
修改LoadSettings方法,直接操作私有字段_singletonSettings,而非通过SettingsContainer属性访问,同时修复File.Create的句柄泄漏问题:
public void LoadSettings() { string filePath = "Config/Settings.txt"; if (!Directory.Exists(Path.GetDirectoryName(filePath))) { Directory.CreateDirectory(Path.GetDirectoryName(filePath)); } Settings loadedSettings = null; if (File.Exists(filePath)) { try { loadedSettings = LoadViaDataContractSerialization<Settings>(filePath); } catch (Exception ex) { Log.GlobalLog.WriteError("LoadViaDataContractSerialization() error:\n" + ex.Message + "\nStackTrace: \n" + ex.StackTrace); } } else { // 用using块释放File.Create返回的文件流,避免后续文件访问异常 using (File.Create(filePath)) {} } // 直接操作私有字段,跳过SettingsContainer的getter逻辑 _singletonSettings = loadedSettings ?? new Settings(); }
进阶优化:使用Lazy实现安全单例
替换手动单例逻辑为Lazy<T>,自动实现线程安全的延迟初始化,彻底避免循环调用问题:
// 替换原有的_singletonSettings字段和SettingsContainer属性 private static readonly Lazy<Settings> _lazySettings = new Lazy<Settings>(() => { var settings = new Settings(); settings.LoadSettings(); return settings; }); public static Settings SettingsContainer => _lazySettings.Value;
内容的提问来源于stack exchange,提问作者Joshua Walker
相关产品推荐
相关产品推荐

