You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET MVC应用中XML异常无法捕获且代码重复执行问题求助

ASP.NET MVC中Settings单例加载错误XML时无限循环且异常捕获失效问题

问题现象

为ASP.NET MVC应用编写的设置处理单例类,在测试格式错误/缺失的XML文件时,出现以下问题:

  • 抛出的XmlException看似无法被捕获,代码无限重复执行同一代码段
  • 异常发生前后的断点被反复触发,异常抛出后的断点无法命中
  • 已禁用Visual Studio调试对话框弹窗,且无手动循环调用属性的逻辑,异常后的默认处理逻辑未生效

重复抛出'System.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);
    }
}

问题根因

  1. 无限循环触发逻辑:当XML反序列化失败,LoadViaDataContractSerialization返回default(T)(即null),执行SettingsContainer = null后,LoadSettings中的if (SettingsContainer == null)会访问SettingsContainer的getter。此时_singletonSettings已被设为null,getter会重新创建Settings实例并调用LoadSettings,形成无限循环。
  2. 异常看似未捕获:实际异常已被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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 06:31:15