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 nameTypedParameter
- 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
andTypedParameter
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.