Passing parameters to constructors using Autofac

You can always use the WithParameter method to explicitly specify a constructor parameter:

builder.RegisterType<DoesSomething>()
       .As<IDoesSomething>()
       .WithParameter("helper", new HelperClass("do", "something"));

builder.RegisterType<DoesSomethingElse>()
       .As<IDoesSomethingElse>()
       .WithParameter("helper", new HelperClass("do", "somethingelse"));

As far as I can tell there is no need for an interface for HelperClass because it essentially is just a value holder.

For this to work you would need to make the internal constructor public, I think.


There are two ways to pass parameters in Autofac:

When you are registering the component:

When you register components, you have the ability to provide a set of parameters that can be used during the resolution of services based on that component. Autofac offers several different parameter matching strategies:

  • NamedParameter - match target parameters by name
  • TypedParameter - match target parameters by type (exact type match required)
  • ResolvedParameter - flexible parameter matching

    // Using a NAMED parameter:
    builder.RegisterType<ConfigReader>()
       .As<IConfigReader>()
       .WithParameter("configSectionName", "sectionName");// parameter name, parameter value. It's the same of this: new NamedParameter("configSectionName", "sectionName")
    
    // Using a TYPED parameter:
    builder.RegisterType<ConfigReader>()
       .As<IConfigReader>()
       .WithParameter(new TypedParameter(typeof(string), "sectionName"));
    
    // Using a RESOLVED parameter:
    builder.RegisterType<ConfigReader>()
       .As<IConfigReader>()
       .WithParameter(
         new ResolvedParameter(
           (pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName",
           (pi, ctx) => "sectionName"));
    

    NamedParameter and TypedParameter can supply constant values only.

    ResolvedParameter can be used as a way to supply values dynamically retrieved from the container, e.g. by resolving a service by name.

In case you want to pass as parameter a service that is already registered, eg, IConfiguration, you can resolve the parameter as I show below:

    builder.RegisterType<Service>()
           .As<Iervice>()
           .WithParameter((pi, ctx) => pi.ParameterType == typeof(IConfiguration) && pi.Name == "configuration",
                          (pi, ctx) => ctx.Resolve<IConfiguration>());

When you are resolving the component:

One way to pass parameter at runtime in Autofac is using the Resolve method. You could create a class like this:

public class ContainerManager
{
  public IContainer Container {get;set;}
  //...
  public T[] ResolveAllWithParameters<T>(IEnumerable<Parameter> parameters)
  {
    return Container.Resolve<IEnumerable<T>>(parameters).ToArray();
  }
}

Parameter is an abstract class that belongs to Autofac, you can use the NamedParameter class to pass the parameters that you need. You can use the ContainerManager class as I show below:

    public T[] ResolveAllWithParameters<T>(IDictionary<string,object> parameters )
    {
        var _parameters=new List<Parameter>();
        foreach (var parameter in parameters)
        {
            _parameters.Add( new NamedParameter(parameter.Key, parameter.Value));
        }
        return ContainerManager.ResolveAllWithParameters<T>(_parameters);
    }

This way you can pass the parameters at runtime using a Dictionary<string, object> when you are resolving an specific component.

Using an Extension Method could be even more simple:

public static class ContainerExtensions
{
    public static T[] ResolveAllWithParameters<T>(this IContainer Container, IDictionary<string, object> parameters)
    {
        var _parameters = new List<Parameter>();
        foreach (var parameter in parameters)
        {
            _parameters.Add(new NamedParameter(parameter.Key, parameter.Value));
        }
        return Container.Resolve<IEnumerable<T>>(_parameters).ToArray();
    }
}

Autofac does not use non-public constructors. By default, it only finds public ones and simply doesn't see the others. Unless you use .FindConstructorsWith(BindingFlags.NonPublic), it will see only public constructors. Therefore your scenario should work as you expect it to do.

Tags:

C#

Autofac