Passing parameters to constructors using Autofac

Solution 1:

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.

Solution 2:

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