3

先週、私の Windows サービス アプリケーションの運用環境へのデプロイが失敗したため、プロジェクトをローカルでリリース モードで実行しようとしたところInvalidOperationException: Can not determine current class.GetCurrentClassLogger.

私のプロジェクトでは、 NinjectILoggerFactoryを使用して、中間層のすべてのサービスに解決します。次に、サービスはコンストラクターでGetCurrentClassLogger()適切なものを取得するために使用します。ILogger例えば:

public class FooService : IFooService
{
     private readonly ILogger logger;

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

     // ... 
}

Ninject.Extensions.LoggingGetCurrentClassLoggerのatLoggerFactoryBaseの実装を掘り下げると:

[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実行されることです。

この問題をコンパイラのコード最適化に関連付けることができました。プロジェクト プロパティの [ビルド] タブで無効にすると、正常に実行されます。

質問StackFrame:の提供を停止する原因は何DeclaringTypeですか?

その間、私はこれを回避策として使用していますが、元の方法を使用することをお勧めします。

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

どんな助けでも大歓迎です。

4

1 に答える 1

4

発行されている CIL を見ないと、動作が異なる理由を正確に言うのは難しいですが、インライン化されていると思います。コンストラクターに次の属性を付けると、目的の結果が得られる場合があります。

[MethodImpl(MethodImplOptions.NoInlining)]

NInject コードが行っているのと同じように。読み取り専用フィールドを割り当てたのは 1 行だけであるため、メソッドがインライン化されている可能性があります。ここでインライン化が StackFrames に与える影響について少し読むことができます: http://www.hanselman.com/blog/ReleaseISNOTDebug64bitOptimizationsAndCMethodInliningInReleaseBuildCallStacks.aspx

とはいえ、この種の予測不可能性が、スタック フレームの使用が悪い考えである理由です。クラス名を指定する必要のない構文が必要で、VS 2012 を使用していて、ファイル名がクラス名と一致する場合は、拡張メソッドで CallerFilePath 属性を使用して、使用せずに現在と同じ動作を維持できます。フレームをスタックします。参照: https://msdn.microsoft.com/en-us/library/hh534540.aspx

アップデート:

別のアプローチとして、ILogger を直接注入し、注入先のコンポーネントの Type を決定する責任をコンテナーに持たせることもできます (確実に認識されているため)。これもきれいに見えます。ここから例を参照してください: 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 に答える