Passing in the type of the declaring class for NLog using Autofac

前端 未结 3 1118
野趣味
野趣味 2021-01-06 08:11

Following on from this question I would like autofac to inject the type of the declaring object into the constructor of my NLog service, so that it can correctly log which t

3条回答
  •  一整个雨季
    2021-01-06 08:33

    The following technique works well in our experience:

    1. Create an attribute like below, which can be applied at class level or at the injection site:

      [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class)]
      public class LoggerAttribute : Attribute
      {
          public readonly string Name;
      
          public LoggerAttribute(string name)
          {
              Name = name;
          }
      }
      
    2. Create an Autofac module that you register with the ContainerBuilder:

      public class LogInjectionModule : Module
      {
          protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration)
          {
              registration.Preparing += OnComponentPreparing;
          }
      
          static void OnComponentPreparing(object sender, PreparingEventArgs e)
          {
              var typePreparing = e.Component.Activator.LimitType;
      
              // By default, the name supplied to the logging instance is the name of the type in which it is being injected into.
              string loggerName = typePreparing.FullName;
      
              //If there is a class-level logger attribute, then promote its supplied name value instead as the logger name to use.
              var loggerAttribute = (LoggerAttribute)typePreparing.GetCustomAttributes(typeof(LoggerAttribute), true).FirstOrDefault();
              if (loggerAttribute != null)
              {
                  loggerName = loggerAttribute.Name;
              }
      
              e.Parameters = e.Parameters.Union(new Parameter[]
              {
                  new ResolvedParameter(
                      (p, i) => p.ParameterType == typeof (Logger),
                      (p, i) =>
                      {
                          // If the parameter being injected has its own logger attribute, then promote its name value instead as the logger name to use.
                          loggerAttribute = (LoggerAttribute)
                          p.GetCustomAttributes(typeof(LoggerAttribute),true).FirstOrDefault();
                          if (loggerAttribute != null)
                          {
                              loggerName = loggerAttribute.Name;
                          }
      
                          // Return a new Logger instance for injection, parameterised with the most appropriate name which we have determined above.
                          return LogManager.GetLogger(loggerName);
                      }),
      
                  // Always make an unamed instance of Logger available for use in delegate-based registration e.g.: Register((c,p) => new Foo(p.TypedAs())
                  new TypedParameter(typeof(Logger), LogManager.GetLogger(loggerName))
              });
          }
      }
      
    3. You can now inject a named Logger in any one of these ways depending on individual scenarios:

      • By default, the injected logger name will be given the full type name of the class it is injected into:

        public class Foo
        {
            public Foo(Logger logger)
            {
            }
        }
        
      • Use a constructor parameter [Logger] attribute to override the logger name:

        public class Foo
        {
            public Foo([Logger("Meaningful Name")]Logger logger)
            {
            }
        }
        
      • Use a class-level [Logger] attribute to set the same logger name override for all constructor overloads:

        [Logger("Meaningful Name")]
        public class Foo
        {
            public Foo(Logger logger, int something)
            {
            }
        
            public Foo(Logger logger, int something, DateTime somethingElse)
            {
            }
        }
        
      • Use constructor parameter [Logger] attributes on each constructor overload to set different logger names depending on the context of how you were constructed:

        public class Foo
        {
            public Foo(Logger("Meaningful Name")]Logger logger, int something)
            {
            }
        
            public Foo(Logger("Different Name")]Logger logger, int something, DateTime somethingElse)
            {
            }
        }
        


    IMPORTANT NOTE: If you register types to be resolved with logger constructor injection using Autofac's delegate registration, you MUST use the two parameter overload like so: Register((c,p) => new Foo(p.TypedAs()).

    Hope this helps!

提交回复
热议问题