简体   繁体   English

如何判断 IRegistrationBuilder.EnableInterfaceInterceptors() 是否已被调用?

[英]How can I tell if IRegistrationBuilder.EnableInterfaceInterceptors() has already been called?

I'm using Autofac.Extras.DynamicProxy to write a couple of IInterceptor s.我正在使用Autofac.Extras.DynamicProxy编写几个IInterceptor They can be used individually, or both together.它们可以单独使用,也可以同时使用。 I want consumers of these interceptors to be able to attach them to Autofac registrations easily, so I wrote an IRegistrationBuilder extension method for each of them:我希望这些拦截器的使用者能够轻松地将它们附加到 Autofac 注册,因此我为它们中的每一个编写了一个IRegistrationBuilder扩展方法:

public static class RegistrationBuilderExtensions
{
    public static IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> WithTelemetryLogging<T, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> builder)
    {
        return builder.EnableInterfaceInterceptors().InterceptedBy(typeof(TelemetryLoggingInterceptor));
    }

    public static IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> WithCorrelationRoots<T, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> builder)
    {
        return builder.EnableInterfaceInterceptors().InterceptedBy(typeof(NewCorrelationInterceptor));
    }
}

The extension methods work well when one or the other is used.当使用一种或另一种时,扩展方法效果很好。 However, if a consumer uses both extension methods, as in:但是,如果消费者使用两种扩展方法,如:

builder.RegisterAssemblyTypes(GetType().Assembly)
    .AsImplementedInterfaces()
    .WithCorrelationRoots()
    .WithTelemetryLogging()
    .PreserveExistingDefaults();

I get an exception about creating a proxy of a proxy:我得到一个关于创建代理的代理的异常:

Castle.DynamicProxy.ProxyGenerationException: This is a DynamicProxy2 error: Target type for the proxy implements Castle.DynamicProxy.IProxyTargetAccessor which is a DynamicProxy infrastructure interface and you should never implement it yourself. Are you trying to proxy an existing proxy?

I believe it's due to EnableInterfaceInterceptors() being called twice, once in each extension method.我相信这是由于EnableInterfaceInterceptors()被调用了两次,一次在每个扩展方法中。

I'd rather not remove the calls to EnableInterfaceInterceptors() from the extension methods and force the consumers to remember to call it themselves.我宁愿不从扩展方法中删除对EnableInterfaceInterceptors()的调用并强制消费者记住自己调用它。 Instead, I'd like to be able to conditionally call it based on whether or not it had already been called.相反,我希望能够根据它是否已经被调用来有条件地调用它。

How I can inspect the IRegistrationBuilder object to determine if this call has already been made, and/or use one of its conditional registration mechanisms?我如何检查IRegistrationBuilder object 以确定是否已进行此调用,和/或使用其条件注册机制之一? Something like:就像是:

if (!builder.EIIHasBeenCalled)
{
    builder.EnableInterfaceInterceptors();
}
return builder.InterceptedBy(...

or或者

builder.EnableInterfaceInterceptors.OnlyIf(b => !b.EIIHasBeenCalled).InterceptedBy(...

You can't inspect the interception bits post-facto.您无法事后检查拦截位。 However, ContainerBuilder has a Properties dictionary that you could use.但是, ContainerBuilder有一个您可以使用的Properties字典。

public static class RegistrationBuilderExtensions
{
  public static IRegistrationBuilder<T, TActivatorData, TRegistrationStyle>
    WithTelemetryLogging<T, TActivatorData, TRegistrationStyle>(
      this IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> builder, ContainerBuilder cb)
  {
    var key = PropertyKey(builder);
    if(!cb.Properties.ContainsKey(key))
    {
      cb.Properties[key] = true;
      return builder
        .EnableInterfaceInterceptors()
        .InterceptedBy(typeof(TelemetryLoggingInterceptor));
    }

    return builder;
  }

  public static IRegistrationBuilder<T, TActivatorData, TRegistrationStyle>
    WithCorrelationRoots<T, TActivatorData, TRegistrationStyle>(
      this IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> builder, ContainerBuilder cb)
  {
    var key = PropertyKey(builder);
    if(!cb.Properties.ContainsKey(key))
    {
      cb.Properties[key] = true;
      return builder
        .EnableInterfaceInterceptors()
        .InterceptedBy(typeof(NewCorrelationInterceptor));
    }

    return builder;
  }

  private static string PropertyKey<T, TActivatorData, TRegistrationStyle>(IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> registration)
  {
    // Uniquely identify the registration however you want.
    return registration.GetType().ToString();
  }
}

Unfortunately, it looks a little messy when you use it because the ContainerBuilder is what has the properties.不幸的是,当您使用它时它看起来有点乱,因为ContainerBuilder是具有属性的东西。 You'd have to pass that in.你必须把它传进去。

builder.RegisterAssemblyTypes(GetType().Assembly)
    .AsImplementedInterfaces()
    .WithCorrelationRoots(builder)
    .PreserveExistingDefaults();

Container building and registration isn't multi-threaded so you won't hit any race conditions with that ContainsKey call.容器构建和注册不是多线程的,因此您不会通过ContainsKey调用遇到任何竞争条件。

There is a caveat on the PropertyKey method - this will basically be unique per registration type but if you have a bunch of services that are identical types, that could be a challenge. PropertyKey方法有一个警告 - 这基本上是每个注册类型唯一的,但如果你有一堆相同类型的服务,那可能是一个挑战。 There is not a good way right now to uniquely identify a registration.目前没有很好的方法来唯一标识一个注册。 I've filed an issue about this on your behalf.我已代表您提交了关于此的问题。 In the meantime, this is one idea on how to do it.与此同时,这是关于如何做到这一点的一个想法。

Inspired by Travis' response, I found a dictionary within IRegistrationBuilder that I'm using with success.受特拉维斯回应的启发,我在IRegistrationBuilder中找到了一本我正在成功使用的字典。 I now have code that looks like this:我现在的代码看起来像这样:

const string MF_ENABLE_INTERFACE_INTERCEPTORS = "MFEnableInterfaceInterceptors";

public static IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> WithTelemetryLogging<T, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> builder)
{
    if (!_AreInterfaceInterceptorsEnabled(builder))
    {
        builder.EnableInterfaceInterceptors();
        _TrackEnableInterfaceInterceptors(builder);
    }
    return builder.InterceptedBy(typeof(TelemetryLoggingInterceptor));
}

public static IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> WithCorrelationRoots<T, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> builder)
{
    if (!_AreInterfaceInterceptorsEnabled(builder))
    {
        builder.EnableInterfaceInterceptors();
        _TrackEnableInterfaceInterceptors(builder);
    }
    return builder.InterceptedBy(typeof(CorrelatedInterceptor));
}

private static bool _AreInterfaceInterceptorsEnabled<T, TActivatorData, TRegistrationStyle>(IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> builder)
{
    if (builder.RegistrationData.Metadata.TryGetValue(MF_ENABLE_INTERFACE_INTERCEPTORS, out var metadata))
    {
        return (bool?)metadata ?? false;
    }
    return false;
}

private static void _TrackEnableInterfaceInterceptors<T, TActivatorData, TRegistrationStyle>(IRegistrationBuilder<T, TActivatorData, TRegistrationStyle> builder)
{
    builder.RegistrationData.Metadata[MF_ENABLE_INTERFACE_INTERCEPTORS] = true;
}

Thanks, Travis, for the inspiration!谢谢特拉维斯的灵感!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM