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

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

我正在使用 Serilog and Autofac 并且我想在解析时间使用 .ForContext() 注册一个 ILogger 并使用 [=11= 注入的对象类型].使用调试器,我可以遍历 IComponentContext 并查看我想要的内容,但是获取它的类型都是内部的,所以我不能(据我所知)在我的代码中实际获取它。我的注册码(如上所述不起作用)如下所示:

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);
});

DefaultResolveRequestContextResolveOperationInitiatingRequest 由于其保护级别均无法访问。

如有任何想法或建议,我们将不胜感激。

鉴于目标是为给定的请求类型注入正确的 ILogger,您可能需要查看 the log4net example in the documentation 并对其进行调整。基本上,它不是尝试 register ILogger,而是将适当的参数添加到 resolve 链中,以便进行设置正确。

感谢@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);
        }
    }
}