是否可以在 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);
});
DefaultResolveRequestContext
、ResolveOperation
和 InitiatingRequest
由于其保护级别均无法访问。
如有任何想法或建议,我们将不胜感激。
鉴于目标是为给定的请求类型注入正确的 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);
}
}
}
我正在使用 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);
});
DefaultResolveRequestContext
、ResolveOperation
和 InitiatingRequest
由于其保护级别均无法访问。
如有任何想法或建议,我们将不胜感激。
鉴于目标是为给定的请求类型注入正确的 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);
}
}
}