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

.NET Framework 4网站加载过多程序集引发CS1647编译错误求助

解决ASP.NET WebForms中因程序集数量过多导致的CS1647编译错误

问题背景

我们维护一个大型CRM WebForms应用,主站通过iframe承载其他模块,站点启动时会加载所有程序集,目前已加载约1850个DLL。新增带有"SITE_NAME"标识的DLL时,触发以下编译错误:

编译错误说明:服务请求所需资源编译期间出错,请查看以下详细错误信息并修改源代码。
编译器错误信息:CS1647: 表达式过长或复杂,无法编译
源错误:[无相关源代码行]
源文件: 行: 0
版本信息:Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.7.3062.0

详细编译输出仅显示所有程序集路径,目前只能通过删除现有文件来避免新增DLL时的错误。我们怀疑问题出在筛选程序集的代码上:

var ourAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList().FindAll(a => a.FullName.Contains("SITE_NAME"));

尝试过拆分变量处理程序集、调整IIS文件大小限制,但都无效;新增不含"SITE_NAME"的DLL不会触发错误。由于代码库庞大,我们需要无需大幅改动的解决方案,相关完整方法代码如下:

public static SystemVersionInfo GetSystemVersionInfo(out MessageSet msg)
{
    msg = null;
    SystemVersionInfo toReturn = new SystemVersionInfo();
    try
    {
        var ourAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList().FindAll(a => a.FullName.Contains("SITE_NAME"));

        List<LibInfo> SITEAssembliesInfo = new List<LibInfo>();
        ourAssemblies.ForEach(a =>
        {
            SITEAssembliesInfo.Add(new LibInfo() { name = a.GetName().Name, version = a.GetName().Version.ToString(), BuildDate = a.GetLinkerTime() });

        });
        toReturn.SITELibs = SITEAssembliesInfo.OrderBy(l => l.name).ToList();

        List<LibInfo> otherAssembliesInfo = new List<LibInfo>();
        var otherAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList().FindAll(a => !a.FullName.Contains("SITE_NAME"));
        otherAssemblies.ForEach(a =>
        {
            otherAssembliesInfo.Add(new LibInfo() { name = a.GetName().Name, version = a.GetName().Version.ToString() });

        });
        toReturn.ThirdPartyLibs = otherAssembliesInfo.OrderBy(l => l.name).ToList();
    }
    catch (Exception ex)
    {
        msg = MessageCreate.CreateErrorMessage(0, ex, "GetSystemVersionInfo", "SITE_NAME.THIS_APP.WEB.dll");
    }

    return toReturn;
}

问题分析

CS1647错误本质是编译器处理的表达式复杂度超出限制。当程序集数量达到1850个时,两次调用FindAll并配合lambda表达式的链式操作,会让JIT编译器生成的表达式树规模过大,最终触发编译失败。同时重复调用AppDomain.CurrentDomain.GetAssemblies()也会加剧资源占用和处理复杂度。

解决方案

以下是无需大幅改动代码的优化方案:

1. 单次遍历程序集,避免重复操作

将两次FindAll合并为一次遍历,同时缓存AssemblyName减少重复调用,降低编译器需要处理的表达式复杂度:

public static SystemVersionInfo GetSystemVersionInfo(out MessageSet msg)
{
    msg = null;
    SystemVersionInfo toReturn = new SystemVersionInfo();
    try
    {
        // 仅获取一次全量程序集列表
        var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
        List<LibInfo> SITEAssembliesInfo = new List<LibInfo>();
        List<LibInfo> otherAssembliesInfo = new List<LibInfo>();

        // 单次遍历完成分类,替代两次FindAll
        foreach (var a in allAssemblies)
        {
            try
            {
                // 缓存AssemblyName,避免重复调用GetName()
                var assemblyName = a.GetName();
                if (a.FullName.Contains("SITE_NAME"))
                {
                    SITEAssembliesInfo.Add(new LibInfo() 
                    { 
                        name = assemblyName.Name, 
                        version = assemblyName.Version.ToString(), 
                        BuildDate = a.GetLinkerTime() 
                    });
                }
                else
                {
                    otherAssembliesInfo.Add(new LibInfo() 
                    { 
                        name = assemblyName.Name, 
                        version = assemblyName.Version.ToString() 
                    });
                }
            }
            catch (Exception)
            {
                // 跳过无法访问的程序集(如动态生成的临时程序集)
                continue;
            }
        }

        toReturn.SITELibs = SITEAssembliesInfo.OrderBy(l => l.name).ToList();
        toReturn.ThirdPartyLibs = otherAssembliesInfo.OrderBy(l => l.name).ToList();
    }
    catch (Exception ex)
    {
        msg = MessageCreate.CreateErrorMessage(0, ex, "GetSystemVersionInfo", "SITE_NAME.THIS_APP.WEB.dll");
    }

    return toReturn;
}

2. 优化程序集匹配逻辑(可选)

如果"SITE_NAME"是程序集名称的固定前缀,改用assemblyName.Name.StartsWith("SITE_NAME")替代FullName.Contains,减少字符串匹配的复杂度,同时避免FullName中版本、公钥等额外信息的干扰。

3. 过滤无效程序集(可选)

跳过动态生成的程序集和无本地文件的程序集,进一步减少处理量:

foreach (var a in allAssemblies)
{
    // 跳过动态程序集和无本地路径的程序集
    if (a.IsDynamic || string.IsNullOrEmpty(a.Location))
        continue;
    
    // 后续分类逻辑...
}

4. 启用站点预编译(可选)

对于WebForms应用,启用ASP.NET预编译可以提前编译站点资源,避免运行时编译的表达式复杂度问题:

  • 使用aspnet_compiler.exe工具预编译站点
  • 在Visual Studio发布时选择"预编译"选项

方案有效性说明

  • 单次遍历替代两次FindAll,大幅降低了JIT编译器需要处理的表达式树规模
  • 缓存AssemblyName减少了重复反射调用的开销
  • 增加异常捕获跳过无效程序集,避免单个程序集的问题影响整体逻辑

内容的提问来源于stack exchange,提问作者Muhammad Nazeer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:35:29