简体   繁体   English

是否可以在 Autofac 的解析时获取请求的服务类型?

[英]Is it possible to get the requesting service type at resolution time in Autofac?

I'm using Serilog and Autofac and I would like to register an ILogger using .ForContext() at resolution time with the type of the object to be injected with the ILogger .我正在使用SerilogAutofac ,我想在解析时使用.ForContext()注册一个ILogger ,并使用ILogger注入的对象类型。 Using the debugger, I can walk the IComponentContext and see what I want but the types to get to it are all internal so I can't (as far as I'm aware) actually get to it in my code.使用调试器,我可以遍历IComponentContext并查看我想要的内容,但是获取它的类型都是内部的,所以我不能(据我所知)实际上在我的代码中得到它。 My registration code (which doesn't work as stated above) looks like this:我的注册码(如上所述不起作用)如下所示:

builder.RegisterType<SerilogLoggerFactory>().As<ISerilogLoggerFactory>().SingleInstance();
builder.Register(context =>
{
    var defaultResolveRequestContext = (Autofac.Core.Resolving.Pipeline.DefaultResolveRequestContext)context;
    var resolveOperation = (Autofac.Core.Resolving.ResolveOperation)defaultResolveRequestContext.Operation;
    var initiatingRequestService = (Autofac.Core.TypedService)resolveOperation.InitiatingRequest.Service;
    return context.Resolve<ISerilogLoggerFactory>().Create().ForContext(initiatingRequestService.ServiceType);
});

DefaultResolveRequestContext , ResolveOperation , and InitiatingRequest are all inaccessible due to their protection level. DefaultResolveRequestContextResolveOperationInitiatingRequest都因为它们的保护级别而无法访问。

Any thoughts or ideas would be appreciated.任何想法或想法将不胜感激。

Given the goal is to inject the right ILogger for a given requesting type, you may want to look at the log4net example in the documentation and adapt it.鉴于目标是为给定的请求类型注入正确的ILogger ,您可能需要查看 文档中的 log4net 示例并对其进行调整。 Basically, rather than trying to register the ILogger , it adds the appropriate parameter to the resolve chain so it'll be set up correctly.基本上,它不是尝试注册ILogger ,而是将适当的参数添加到解析链中,以便正确设置。

Thanks to @Travis Illig's response , we created the following implementation:感谢@Travis Illig 的回复,我们创建了以下实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using Autofac.Core;
using Autofac.Core.Resolving.Pipeline;

using Serilog;

namespace YOUR_COMPANY_NAME.Core.Logging.Serilog;

/// <summary>
/// Injects resolved types with <see cref="ILogger.ForContext(Type)"/> for <see cref="ILogger"/>.
/// Supports both constructor and property injection.
/// Inspired by <see href="link">https://autofac.readthedocs.io/en/latest/examples/log4net.html</see>.
/// </summary>
public class AutofacSerilogMiddleware : IResolveMiddleware
{
    private readonly ILogger _rootLogger;
    private readonly Dictionary<Type, List<PropertyInfo>> _loggerPropertiesCache = new();

    public PipelinePhase Phase => PipelinePhase.ParameterSelection;

    public AutofacSerilogMiddleware(ILogger rootLogger)
    {
        _rootLogger = rootLogger;
    }

    public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
    {
        context.ChangeParameters(context.Parameters.Union(new[] { new ResolvedParameter((p, i) => p.ParameterType == typeof(ILogger), (p, i) => p.Member.DeclaringType != null ? _rootLogger.ForContext(p.Member.DeclaringType) : _rootLogger), }));

        // Continue the resolve.
        next(context);

        // Has an instance been activated?
        if (!context.NewInstanceActivated || context.Instance == null)
        {
            return;
        }

        var instanceType = context.Instance.GetType();

        if (!_loggerPropertiesCache.TryGetValue(instanceType, out var propertyInfos))
        {
            // Get all the injectable properties to set.
            propertyInfos = instanceType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.PropertyType == typeof(ILogger) && p.CanWrite && p.GetIndexParameters().Length == 0).ToList();

            _loggerPropertiesCache[instanceType] = propertyInfos;
        }

        if (!propertyInfos.Any())
        {
            return;
        }

        var contextLogger = _rootLogger.ForContext(instanceType);

        foreach (var propertyInfo in propertyInfos)
        {
            //Performance could be improved by generating and caching setter delegates instead of using PropertyInfo.SetValue
            propertyInfo.SetValue(context.Instance, contextLogger, null);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM