6

这个问题更多的是关于 C#,而不是关于 log4net(我认为)。

我创建了一个自定义附加程序,并让它读取程序之前设置的静态字段。

令我惊讶的是,静态字段被重新初始化,并且设置值没有进入附加程序。

我启动了 debugview 并看到静态构造函数被调用了两次(!)。这在同一个appdomain中不应该是可能的吧?由于 VS 没有在断点上第二次命中,因此只有 debugview 才能揭示这一点。

请注意,这不是关于避免在 log4net 中使用静态变量的问题。我对 log4net 使用什么样的魔法来完成这个感兴趣?

编辑#1

你好乔恩,大粉丝。

我按照要求进一步隔离了它。首先,我从空白开始,朝着暴露错误的目标情况努力。由于我几乎逐个匹配目标字符并且仍然没有复制,所以我反其道而行之。

从错误情况开始,我删除了所有我认为不重要的东西,直到它开始......按预期工作。

当运行时尝试解析 log4net 程序集时,似乎有一些奇怪的事情发生(在调试模式下观察到)

这是我在调试视图中看到的:

[7756] 常规:警告 - 无法解析模块的“log4net”版本。异常:System.NullReferenceException:对象引用未设置为对象的实例。[7756] 在 DebuggerShared.Services.EventArgs.ModuleLoadedInDebuggerEventArgs..ctor(String modulePath, String moduleLoadMessage, Boolean isUserCode, String name, String version) [7756] General: WARN - Failed to parse module's 'FollowUp.Common' version。异常:System.NullReferenceException:对象引用未设置为对象的实例。[7756] 在 DebuggerShared.Services.EventArgs.ModuleLoadedInDebuggerEventArgs..ctor(字符串 modulePath,字符串 moduleLoadMessage,布尔 isUserCode,字符串名称,字符串版本)

并且 VS 在调试模块屏幕中没有显示路径值。现在我是怎么做到的呢?奇怪的是它设法加载了一个程序集,但不能再告诉从哪里 :)

这是孤立的情况,如果我进一步修改它,它就会开始按预期工作。

https://www.sugarsync.com/pf/D6486369_1701716_00940

我仍然对技术细节感兴趣,但是在删除对 log4net 的引用并再次添加它之后,一切又开始工作了。我很高兴它有效,但它让我烦恼的是我没有一个彻底的解释

此外,静态构造函数现在调用了两次,这是有道理的,因为当 log4net 得到它时,类型会再次初始化。

我认为在这个问题上花费更多时间是不值得的,因为我认为解决方案处于一种奇怪的状态,并且理解所有这些都具有边际价值。不过,如果你能想到一些东西来解释这一点,我很乐意在这里。

编辑#2

事实证明,某些程序集确实被加载了两次,包括带有静态构造函数的程序集。稍后我将研究这是如何实现的,但我有一个解决方法,即禁用和启用 Costura。Costura 是一项将所有程序集合并为一个的 msbuild 任务。我并不是说 Costura 是根本原因。csproj/sln 文件很容易处于奇怪的状态。

考虑在未来如何更快地诊断这个问题,我启动了 sysinternals ProcessExplorer。现在我希望看到程序集只加载一次,但我发现它们被加载了两次。似乎这是运行时中的一个错误,仅在 .NET 4 中修复

http://forum.sysinternals.com/why-some-net-assemblies-are-duplicated-in-memory_topic15279.html https://connect.microsoft.com/VisualStudio/feedback/details/467560/clr-maps-assemblies -进入虚拟地址空间两次

编辑#3 Costura 使程序集加载了两次。项目所有者在同一天修复了该问题 :) http://code.google.com/p/costura/issues/detail?id=17&thanks=17&ts=1328826304

我们需要一个 Costura 标签,但我没有必要的 1500 声望点。如果您有权利,请创建它。谢谢。

亲切的问候,汤姆

4

2 回答 2

5

看起来您设法将两个单独的实例加载log4net到同一个AppDomain.

一项目参考:

<Reference Include="log4net">
  <HintPath>..\packages\log4net.1.2.11\lib\net35-full\log4net.dll</HintPath>
</Reference>

另一个:

<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\ExternalReferences\log4net.dll</HintPath>
</Reference>

其中一个是强命名的,另一个不是,这导致 .net 赋予它们不同的身份。提示路径也不同。似乎还有一个1.2.10,另一个1.2.11

尝试调用AppDomain.GetAssemblies()并检查是否log4net发生两次。

于 2012-02-08T14:50:43.590 回答
1

那么它可以明确地调用类型初始化器:

var initializer = typeof(Foo).TypeInitializer;
initializer.Invoke(null);

但是,我希望它不会那样做。你能想出一个简短但完整的程序来演示这种情况吗?

于 2012-02-08T11:36:30.620 回答