我在Autofac DynamicProxy2中使用接口拦截器,并且我能够为每个寄存器启用接口拦截器:
var registration = builder.RegisterType<AType>().As<AInterface>();
registration.EnableInterfaceInterceptors().InterceptedBy<AInterceptor>()
我想对所有注册类型应用某些特征。类似的东西:
var registrations = builder.GetAllRegistrations(); // ops, this does not exist...
foreach (var registration in registrations) {
registration.EnableInterfaceInterceptors().InterceptedBy<AInterceptor>()
}
我无法找到获得所有注册的方法。我知道我们可以做到:
builder.RegisterCallback(cr =>
{
foreach (var registration in cr.Registrations)
{
// registration is IComponentRegistration
}
});
但此处的注册时间为IComponentRegistration
,我需要IRegistrationBuilder
才能应用EnableInterfaceInterceptors()
。
答案 0 :(得分:7)
您可以动态添加拦截器,但需要一些工作。要做的是创建一个附加到所有组件注册的自定义Autofac.Module
。我将在一个例子中向您展示。
您无法在全球范围内真正做到EnableInterfaceInterceptors
。我会在示例结尾处找到它。
首先,示例设置:我们有一个简单的接口,一个简单的实现,以及一个处理日志记录调用的拦截器。 (我偷了拦截器代码from the Autofac wiki):
public interface IInterface
{
void DoWork();
}
public class Implementation : IInterface
{
public void DoWork()
{
Console.WriteLine("Implementation doing work.");
}
}
public class CallLogger : IInterceptor
{
TextWriter _output;
public CallLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
_output.WriteLine("Calling method {0} with parameters {1}... ",
invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
invocation.Proceed();
_output.WriteLine("Done: result was {0}.", invocation.ReturnValue);
}
}
我们希望使用我们的调用记录器拦截所有内容(已启用拦截器)。我们通过创建一个自定义Autofac.Module
来实现这一点,它们都使用Autofac注册拦截器并附加到组件动态注册以添加拦截器元数据。
警告:这里有一点点黑客行为可行,但有点像是在搜索数据&#34;进入一个众所周知的位置。它有效,但我不知道它为什么会发生变化,但请注意,因为它有点像是在私人化身上工作。这些东西,这可能会在未来的版本中破裂。请注意。
好的,免责声明完成了。这是模块:
public class InterceptorModule : Autofac.Module
{
// This is a private constant from the Autofac.Extras.DynamicProxy2 assembly
// that is needed to "poke" interceptors into registrations.
const string InterceptorsPropertyName = "Autofac.Extras.DynamicProxy2.RegistrationExtensions.InterceptorsPropertyName";
protected override void Load(ContainerBuilder builder)
{
// Register global interceptors here.
builder.Register(c => new CallLogger(Console.Out));
}
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
// Here is where you define your "global interceptor list"
var interceptorServices = new Service[] { new TypedService(typeof(CallLogger)) };
// Append the global interceptors to any existing list, or create a new interceptor
// list if none are specified. Note this info will only be used by registrations
// that are set to have interceptors enabled. It'll be ignored by others.
object existing;
if (registration.Metadata.TryGetValue(InterceptorsPropertyName, out existing))
{
registration.Metadata[InterceptorsPropertyName] =
((IEnumerable<Service>)existing).Concat(interceptorServices).Distinct();
}
else
{
registration.Metadata.Add(InterceptorsPropertyName, interceptorServices);
}
}
}
要使其工作,请将模块与其余依赖项一起注册。对于这个例子,它看起来像: var builder = new ContainerBuilder();
// Notice this registration doesn't include
// the interceptor - that gets added by the
// module.
builder.RegisterType<Implementation>()
.As<IInterface>()
.EnableInterfaceInterceptors();
// Here's the magic module:
builder.RegisterModule<InterceptorModule>();
var container = builder.Build();
如果您运行这些注册并解决...
var impl = container.Resolve<IInterface>();
impl.DoWork();
您可以看到拦截器正常工作,因为您将看到控制台输出:
Calling method DoWork with parameters ...
Implementation doing work.
Done: result was .
(这有点奇怪,因为我的示例中有一个无参数/无效方法,但拦截器正在工作!)
关于EnableInterfaceInterceptors
来电... 做EnableInterfaceInterceptors
或EnableClassInterceptors
实际上会在后端执行大量疯狂的DynamicProxy2工作。它为组件上的激活事件添加了一些非平凡的事件处理程序,它们将对象包装在动态代理中。这些事件处理程序目前不会单独使用,我不知道尝试并附加所有这些事情需要做多少工作&#34;事后&#34;我们在这里与实际拦截器一起做的方式。
欢迎您自己尝试一下 - the source is on GitHub。但是,基本上,当&#34;添加一个全局拦截器&#34;事情很有效,在一个模块中做全局EnableInterfaceInterceptors
远远不够。你绝对可以自己选择那个。
答案 1 :(得分:6)
在我们执行注册表的那一刻,放弃EnableInterfaceInterceptors
调用显然是微不足道的。
由于我的目标是实现通过Autofac解决的所有接口共有的拦截器,我最终推出了自己的解决方案,灵感来自Autofac的DynamicProxy
实现。
我们的想法是覆盖Autofac Activating
事件并手动创建DynamicProxy。这里的大部分工作是确保我们可以安全地代理正在解决的类型。
public static class AutofacExtensions
{
// DynamicProxy2 generator for creating proxies
private static readonly ProxyGenerator generator = new ProxyGenerator();
/// <summary>
/// Intercept ALL registered interfaces with provided interceptors.
/// Override Autofac activation with a Interface Proxy.
/// Does not intercept classes, only interface bindings.
/// </summary>
/// <param name="builder">Contained builder to apply interceptions to.</param>
/// <param name="interceptors">List of interceptors to apply.</param>
public static void InterceptInterfacesBy(this ContainerBuilder builder, params IInterceptor[] interceptors)
{
builder.RegisterCallback((componentRegistry) =>
{
foreach (var registration in componentRegistry.Registrations)
{
InterceptRegistration(registration, interceptors);
}
});
}
/// <summary>
/// Intercept a specific component registrations.
/// </summary>
/// <param name="registration">Component registration</param>
/// <param name="interceptors">List of interceptors to apply.</param>
private static void InterceptRegistration(IComponentRegistration registration, params IInterceptor[] interceptors)
{
// proxy does not get allong well with Activated event and registrations with Activated events cannot be proxied.
// They are casted to LimitedType in the IRegistrationBuilder OnActivated method. This is the offending Autofac code:
//
// public IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> OnActivated(Action<IActivatedEventArgs<TLimit>> handler)
// {
// if (handler == null) throw new ArgumentNullException("handler");
// RegistrationData.ActivatedHandlers.Add(
// (s, e) => handler(new ActivatedEventArgs<TLimit>(e.Context, e.Component, e.Parameters, (TLimit)e.Instance)));
// return this;
// }
Delegate[] handlers = GetActivatedEventHandlers(registration);
if (handlers.Any(h => handlers[0].Method.DeclaringType.Namespace.StartsWith("Autofac")))
{
return;
}
registration.Activating += (sender, e) =>
{
Type type = e.Instance.GetType();
if (e.Component.Services.OfType<IServiceWithType>().Any(swt => !swt.ServiceType.IsInterface || !swt.ServiceType.IsVisible) ||
// prevent proxying the proxy
type.Namespace == "Castle.Proxies")
{
return;
}
var proxiedInterfaces = type.GetInterfaces().Where(i => i.IsVisible).ToArray();
if (!proxiedInterfaces.Any())
{
return;
}
// intercept with all interceptors
var theInterface = proxiedInterfaces.First();
var interfaces = proxiedInterfaces.Skip(1).ToArray();
e.Instance = generator.CreateInterfaceProxyWithTarget(theInterface, interfaces, e.Instance, interceptors);
};
}
/// <summary>
/// Get Activated event handlers for a registrations
/// </summary>
/// <param name="registration">Registration to retrieve events from</param>
/// <returns>Array of delegates in the event handler</returns>
private static Delegate[] GetActivatedEventHandlers(IComponentRegistration registration)
{
FieldInfo eventHandlerField = registration.GetType().GetField("Activated", BindingFlags.NonPublic | BindingFlags.Instance);
var registrations = eventHandlerField.GetValue(registration);
System.Diagnostics.Debug.WriteLine(registrations);
return registrations.GetType().GetMethod("GetInvocationList").Invoke(registrations, null) as Delegate[];
}
}
答案 2 :(得分:1)
通过利用构建器模式,可以非常轻松地将拦截器添加到所有构建器中。就像这样:
var interceptedBuilders = new List<IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle>>();
ContainerBuilder builder = new ContainerBuilder();
interceptedBuilders.Add(builder.RegisterType<First>().As<IFirst>());
interceptedBuilders.Add(builder.RegisterType<Second>().As<ISecond>());
foreach (var x in interceptedBuilders)
{ x.EnableInterfaceInterceptors().InterceptedBy(typeof(AInterceptor));
}