簡體   English   中英

如何判斷 IRegistrationBuilder.EnableInterfaceInterceptors() 是否已被調用?

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

我正在使用Autofac.Extras.DynamicProxy編寫幾個IInterceptor 它們可以單獨使用,也可以同時使用。 我希望這些攔截器的使用者能夠輕松地將它們附加到 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));
    }
}

當使用一種或另一種時,擴展方法效果很好。 但是,如果消費者使用兩種擴展方法,如:

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

我得到一個關於創建代理的代理的異常:

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?

我相信這是由於EnableInterfaceInterceptors()被調用了兩次,一次在每個擴展方法中。

我寧願不從擴展方法中刪除對EnableInterfaceInterceptors()的調用並強制消費者記住自己調用它。 相反,我希望能夠根據它是否已經被調用來有條件地調用它。

我如何檢查IRegistrationBuilder object 以確定是否已進行此調用,和/或使用其條件注冊機制之一? 就像是:

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

或者

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

您無法事后檢查攔截位。 但是, 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();
  }
}

不幸的是,當您使用它時它看起來有點亂,因為ContainerBuilder是具有屬性的東西。 你必須把它傳進去。

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

容器構建和注冊不是多線程的,因此您不會通過ContainsKey調用遇到任何競爭條件。

PropertyKey方法有一個警告 - 這基本上是每個注冊類型唯一的,但如果你有一堆相同類型的服務,那可能是一個挑戰。 目前沒有很好的方法來唯一標識一個注冊。 我已代表您提交了關於此的問題。 與此同時,這是關於如何做到這一點的一個想法。

受特拉維斯回應的啟發,我在IRegistrationBuilder中找到了一本我正在成功使用的字典。 我現在的代碼看起來像這樣:

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;
}

謝謝特拉維斯的靈感!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM