ILMerge合并程序集后NLog自定义Target无法加载问题排查
问题梳理
我完全理解你遇到的困扰:你开发了包含自定义TargetWithLayout的程序集MyTargets.NLog.dll,其中的MyTestTarget在.NET 4.6.2 + NLog 4.5.3环境下,通过NLog配置的<extensions>节点加载、<target name="my" type="MyTest" />配置时完全正常。但为了打包成单文件,你用ILMerge合并所有依赖后,NLog突然报错找不到这个自定义Target——虽然合并后的程序集能正常加载,dotPeek也确认Target定义没变化,甚至尝试修改NLog源码添加注册方法,还是卡在找不到TargetAttribute上。
问题本质
这其实是程序集类型冲突导致的核心问题:你把NLog也合并进了自己的程序集,但运行时NLog可能还会从全局程序集缓存(GAC)或者其他路径加载一份官方的NLog程序集。此时,自定义Target上标记的TargetAttribute来自合并包里的NLog,而NLog主程序在扫描时,只会识别自身程序集里的TargetAttribute类型——两个TargetAttribute看似同名,实则属于不同的程序集,反射判断时会认为不匹配,自然就识别不了你的自定义Target。
可行解决方案
1. 不合并NLog程序集(最省心的方案)
ILMerge的时候排除NLog.dll,让你的应用单独引用NLog官方包,确保运行时只有一份NLog程序集。这样自定义Target上的属性和NLog主程序的属性是同一个类型,反射就能正常识别。
用ILMerge的/exclude参数即可,命令示例:
ILMerge.exe /out:MergedAssembly.dll MyTargets.NLog.dll /exclude:NLog.dll
2. 手动注册自定义Target,绕开属性扫描
放弃NLog的自动扫描机制,在应用启动阶段用代码手动注册你的Target:
// 应用初始化时执行这段代码 NLog.Config.ConfigurationItemFactory.Default.Targets.RegisterDefinition("MyTest", typeof(MyTestTarget));
这样NLog直接知晓这个Target类型,不需要依赖反射去扫描TargetAttribute,自然避开了类型冲突的问题。
3. 升级到.NET Core/5+使用官方单文件发布(如果项目允许)
如果你的项目能升级到更高版本的.NET,官方的单文件发布功能比ILMerge靠谱得多,它会自动处理依赖加载的问题,不会出现这种类型冲突。只需在.csproj中添加配置:
<PropertyGroup> <PublishSingleFile>true</PublishSingleFile> <SelfContained>true</SelfContained> </PropertyGroup>
4. 配置绑定重定向强制使用合并后的NLog
如果一定要合并NLog,那得确保所有对NLog的引用都指向合并后的版本,防止加载外部的NLog。在合并后的程序集对应的配置文件里添加绑定重定向:
<runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.5.3" newVersion="4.5.3" /> </dependentAssembly> </assemblyBinding> </runtime>
同时要保证程序运行目录里没有单独的NLog.dll,避免被意外加载。
总结
优先推荐方案1或者方案2,这两个改动最小,也最不容易出问题——要么让NLog保持独立,要么直接告诉NLog你的Target类型,都能完美避开这个类型冲突的坑。
内容的提问来源于stack exchange,提问作者AFract

