在解析过程中确定依赖项的目标类型



似乎无法确定解析依赖项的类型:

containerBuilder.Register(context =>
{
// What is the type for which this component is resolved?
var type = default(Type); // TBD
return context.Resolve<ILoggerFactory>().CreateLogger(type);
});

这里的目标是创建一个.NET核心记录器,该记录器具有适用于其类型的正确类别。

Autofac文档中的例子描述了如何使用中间件组件实现这一点,我很成功。但似乎在每个注册中添加一个管道都会影响性能,而且我还没有发现一种方法,只将管道应用于依赖ILogger的组件的注册。

动机:显而易见的选择似乎是将依赖关系更改为ILogger<T>类型,其中T是应用该依赖关系的类型,如下所示:

public class Component
{
public Component(ILogger<Component> logger)...
}

但经验告诉我,许多开发人员匆忙地复制和粘贴组件,忘记更改类型参数,导致日志混乱。在当前的代码中,我们仍然使用Common.Logging,我们的组件只需要一个非通用的ILog:

public class Component
{
public Component(ILog log)...
}

在我们以前的DI容器Castle。Windsor中,它会像这样简单:

public class LoggerSubDependencyResolver : ISubDependencyResolver
{
public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
{
return dependency.TargetType == typeof(ILog);
}
public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
{
return CanResolve(context, contextHandlerResolver, model, dependency) ? LogManager.GetLogger(model.Implementation) : null;
}
}

有没有更简单的方法来实现这一点?或者就是这样,而我过于担心性能影响?

是的,但它没有记录,所以使用它的风险由您自己承担

containerBuilder.Register(ctx =>
{
var rc = ctx as ResolveRequestContext;    
var operation = rc.Operation as IDependencyTrackingResolveOperation;
//this is not going to work for controllers, unless you register them as services
var service = operation.RequestStack.Skip(1).First().Service as TypedService;   
return LogManager.GetLogger(service.ServiceType);
});

如果您坚持使用文档,中间件方法就是实现这一点的方法。在这种情况下,它几乎是CastleWindsor解析器的直接替代方案(注意:在CW中,每个寄存器也会调用解析器(。并且您可以使用反射仅为依赖ILog的类设置中间件。此外,如果性能有问题,您可能需要缓存文档中提到的LogManager.GetLogger调用。

public class Log4NetMiddleware : IResolveMiddleware
{
//Caching LogManager.GetLogger(type)
private ILog _log;
public Log4NetMiddleware(ILog log)
{            
_log = log;
}
public PipelinePhase Phase => PipelinePhase.ParameterSelection;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
context.ChangeParameters(context.Parameters.Union(
new[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(ILog), //This is your CanResolve
(p, i) => _log //Resolve
),
}));
next(context);
//This code below can be removed if you don't need injection via properties
if (context.NewInstanceActivated)
{
var instanceType = context.Instance.GetType();
//This is your CanResolve
var properties = instanceType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);
foreach (var propToSet in properties)
{
//This is your Resolve
propToSet.SetValue(context.Instance, _log, null);
}
}
}
}

注册看起来像这个

public void ConfigureContainer(ContainerBuilder containerBuilder)
{
containerBuilder.ComponentRegistryBuilder.Registered += (sender, args) =>
{
var type = args.ComponentRegistration
.Activator
.LimitType;
var constructors = type
.GetConstructors(BindingFlags.Instance | BindingFlags.Public);
if (constructors.Any(x => x.GetParameters().Any(p => p.ParameterType == typeof(ILog))))
{
args.ComponentRegistration.PipelineBuilding += (sender2, pipeline) =>
{
pipeline.Use(new Log4NetMiddleware(LogManager.GetLogger(type)));
};
return;
}
//the code below can be removed if you don't inject via properties
var properties = type
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);
if (properties.Any())
{
args.ComponentRegistration.PipelineBuilding += (sender2, pipeline) =>
{
pipeline.Use(new Log4NetMiddleware(LogManager.GetLogger(type)));
};
}
};
}

最新更新