Search code examples
autofacautofac-module

How do you provide specific registrations in Autofac as parameters to other registrations?


Can you plase provide some guidance on how to pass some references to other registrations ?

//registration of 1st http client
    builder.RegisterType<HttpClient>()
      //.Keyed<HttpMessageInvoker>("authHttpClient")
     .WithProperties(new[] { new NamedPropertyParameter("PooledConnectionLifetime", 2 })
     .WithProperties(new[] { new NamedPropertyParameter("BaseAddress", "whatever"))})
     .SingleInstance();

//registration of 2nd httpclient
    builder.RegisterType<HttpClient>()
     .Keyed<HttpMessageInvoker>("dynamicsHttpClient")
     .WithProperties(new[] { new NamedPropertyParameter("PooledConnectionLifetime", 20) })
     .WithProperties(new[] { new NamedPropertyParameter("BaseAddress", "other something" })
    .SingleInstance();


// **I need to  do the registration of type and pass the 1st httpClient registration**
builder.RegisterType<DynamicsAuthApiGateway>()
    .As<IDynamicsAuthApiGateway>()

// **I need to do the registration of type pass 2nd instance of httpClient registration**
builder.RegisterType<DynamicsApiGateway>()
                .As<IDynamicsApiGateway>()
                .SingleInstance();

//Method ctor's
//DynamicsAuthApiGateway(**HttpClient client**, DynamicsAuthApiGatewaySettings apiGatewaySettings)
//DynamicsApiGateway(**HttpClient client**, Func<HttpResponseMessage, Task> errorHandler = null) 

Can you help on how to achieve that ?

Any help would be appreciated ?

Thanks, ME


Solution

  • You're on the right track with using keyed services. Docs here for details and examples.

    The easiest way will be to use the Autofac.Features.AttributeFilters.KeyFilterAttribute to just mark up the constructor on the consuming class.

    First, update the consumers. Here's an example.

    public class DynamicsAuthApiGateway : IDynamicsAuthApiGateway
    {
      // KeyFilterAttribute on the parameter
      public DynamicsAuthApiGateway([KeyFilter("authHttpClient")] HttpClient client)
      {
        // ...
      }
    }
    

    Then register the keyed clients and enable the attribute filtering on the consumers.

    // Don't forget
    // using Autofac.Features.AttributeFilters;
    // at the top... then:
    
    // Register the HttpClient instances with keys
    // and be sure the key is on the same type/interface
    // as you see in the constructor of the consumer.
    builder.RegisterType<HttpClient>()
           .Keyed<HttpClient>("authHttpClient")
           .SingleInstance();
    builder.RegisterType<HttpClient>()
           .Keyed<HttpClient>("dynamicsHttpClient")
           .SingleInstance();
    
    // Register the consumers and enable attribute filters
    builder.RegisterType<DynamicsAuthApiGateway>()
           .As<IDynamicsAuthApiGateway>()
           .WithAttributeFiltering();
    builder.RegisterType<DynamicsApiGateway>()
           .As<IDynamicsApiGateway>()
           .SingleInstance()
           .WithAttributeFiltering();
    

    You have to opt into attribute filtering because it's extra perf overhead if you don't need it. If you don't put the WithAttributeFiltering() part, the filtering won't happen.

    If you don't want that attribute in your class (e.g., you want to avoid tying Autofac into things) then it's a little harder - you'll need to use ResolvedParameter (docs here).

    That's going to look more like this (I'm kinda writing it off the top of my head, so if there's a typo or something, you've been warned, but it should be basically)...

    // Register the HttpClient instances with keys
    // and be sure the key is on the same type/interface
    // as you see in the constructor of the consumer.
    builder.RegisterType<HttpClient>()
           .Keyed<HttpClient>("authHttpClient")
           .SingleInstance();
    
    // Register the consumers using resolved parameters.
    builder.RegisterType<DynamicsAuthApiGateway>()
           .As<IDynamicsAuthApiGateway>()
           .WithParameter(
             new ResolvedParameter(
               (pi, ctx) => pi.ParameterType == typeof(HttpClient),
               (pi, ctx) => ctx.ResolveKeyed<HttpClient>("authHttpClient")));
    

    It's not quite as clean but that keeps the Autofac references out of the consumers (if that matters). If you use that a lot, you could write your own extension method to make it easier. I'll leave that as an exercise for the reader.