3

上周将我的 Windows 服务应用程序部署到生产环境失败了,所以我尝试在本地以发布模式运行项目InvalidOperationException: Can not determine current class.,结果可以追溯到对GetCurrentClassLogger.

我的项目使用Ninject解析ILoggerFactory到中间层的每个服务。然后服务用于在构造函数GetCurrentClassLogger()中获取正确ILogger的。例如:

public class FooService : IFooService
{
     private readonly ILogger logger;

     public FooService(ILoggerFactory loggerFactory)
     {
          this.logger = loggerFactory.GetCurrentClassLogger();
     }

     // ... 
}

如果我深入研究GetCurrentClassLoggeratLoggerFactoryBaseNinject.Extensions.Logging的实现:

[MethodImpl(MethodImplOptions.NoInlining)]
public ILogger GetCurrentClassLogger()
{
  StackFrame stackFrame = new StackFrame(0, false);
  if (stackFrame.GetMethod() == (MethodBase) LoggerFactoryBase.getCurrentClassLoggerMethodInfo)
    stackFrame = new StackFrame(1, false);
  Type declaringType = stackFrame.GetMethod().DeclaringType;
  if (declaringType == (Type) null)
    throw new InvalidOperationException(string.Format("Can not determine current class. Method: {0}", (object) stackFrame.GetMethod()));
  return this.GetLogger(declaringType);
}

我可以看到抛出异常的位置。

我的第一直觉是检查是否有任何系统、框架或项目更新导致它,但自上次成功部署以来没有做任何有意义的事情。

现在......关于这个问题的“有趣”的事情是,当我Trace.WriteLine("Test");在构造函数中添加一行时,GetCurrentClassLogger执行得很好。

我能够将此问题与编译器的代码优化联系起来。如果我在项目属性的Build选项卡中禁用它,它也可以正常执行。

问题:什么可能导致StackFrame停止提供DeclaringType

同时,我将其用作解决方法,但我更喜欢使用原始方法:

this.logger = loggerFactory.GetLogger(this.GetType());

任何帮助将非常感激。

4

1 回答 1

4

如果不查看发出的 CIL,很难确切地说出为什么行为不同,但我猜它正在被内联。如果您将构造函数归因于:

[MethodImpl(MethodImplOptions.NoInlining)]

正如 NInject 代码所做的那样。该方法可能会被内联,因为它只是分配了一个只读字段的单行。您可以在此处阅读有关内联如何影响 StackFrames 的一些信息:http ://www.hanselman.com/blog/ReleaseISNOTDebug64bitOptimizationsAndCMethodInliningInReleaseBuildCallStacks.aspx

也就是说,这种不可预测性就是为什么使用堆栈帧是一个坏主意的原因。如果您想要不必指定类名的语法,正在使用 VS 2012,并且您的文件名与您的类名匹配,您可以在扩展方法上使用 CallerFilePath 属性并保留与现在相同的行为,而无需使用堆栈帧。请参阅: https ://msdn.microsoft.com/en-us/library/hh534540.aspx

更新:

另一种方法可能是直接注入 ILogger 并让您的容器负责确定要注入的组件的类型(因为它肯定知道)。这似乎也更干净。请参阅此处的示例:https ://github.com/ninject/ninject.extensions.logging/issues/19

    this.Bind<ILogger>().ToMethod(context =>
    {
        var typeForLogger = context.Request.Target != null
                                ? context.Request.Target.Member.DeclaringType
                                : context.Request.Service;
        return context.Kernel.Get<ILoggerFactory>().GetLogger(typeForLogger);
    });
于 2015-12-22T03:42:40.690 回答