在解析期间确定依赖项的目标类型
Determine the target type for a dependency during resolution
似乎无法确定解析依赖项的类型:
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 Core 记录器,并为其应用的类型设置正确的类别。
Autofac documentation 中的示例描述了如何使用中间件组件完成此操作,我成功了。但是似乎向每个注册添加管道都会对性能产生影响,而且我还没有发现一种方法可以仅将管道应用于依赖于 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 调用,如前所述 in the documentation.
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)));
};
}
};
}
似乎无法确定解析依赖项的类型:
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 Core 记录器,并为其应用的类型设置正确的类别。
Autofac documentation 中的示例描述了如何使用中间件组件完成此操作,我成功了。但是似乎向每个注册添加管道都会对性能产生影响,而且我还没有发现一种方法可以仅将管道应用于依赖于 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 调用,如前所述 in the documentation.
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)));
};
}
};
}