Передача параметров конструкторам с помощью Autofac
Я очень новичок в autofac, поэтому возможно, что я полностью злоупотребляю им.
предположим, у меня есть класс, который имеет такую структуру:
public class HelperClass : IHelperClass
{
public HelperClass(string a, string b)
{
this.A = a;
this.B = b;
}
}
и у меня есть два класса, которые используют этот класс, но требуют различные значения по умолчанию для конструктора. Второй конструктор предназначен только для тестирования-мы всегда будем хотеть HelperClass в "реальном" приложении.:
public class DoesSomething: IDoesSomething
{
public DoesSomething()
: this(new HelperClass("do", "something"));
{
}
internal DoesSomething(IHelperClass helper)
{
this.Helper = helper;
}
}
public class DoesSomethingElse : IDoesSomethingElse
{
public DoesSomethingElse()
: this(new HelperClass("does", "somethingelse"));
{
}
internal DoesSomethingElse(IHelperClass helper)
{
this.Helper = helper;
}
}
вот мой модуль AutoFac:
public class SomethingModule: Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<DoesSomething>().As<IDoesSomething>();
builder.RegisterType<DoesSomethingElse>().As<IDoesSomethingElse();
}
}
мой вопрос(ы):
- когда я вызываю resolve на DoesSomething или DoesSomethignElse -- будет ли он разрешать внутренний конструктор вместо общедоступного? Нужно ли оставлять IHelperClass незарегистрированным?
- если да, то как заставить его передавать разные параметры каждому экземпляру IHelperClass в зависимости от того, используется ли он в DoesSomething или DoesSomethingElse?
3 ответов
Autofac не использует непубличные конструкторы. По умолчанию он находит только общедоступные и просто не видит других. Если вы не используете .FindConstructorsWith(BindingFlags.NonPublic)
, Он будет видеть только общественные конструкторы. Поэтому ваш сценарий должен работать так, как вы ожидаете.
вы всегда можете использовать WithParameter
метод явного указания параметра конструктора:
builder.RegisterType<DoesSomething>()
.As<IDoesSomething>()
.WithParameter("helper", new HelperClass("do", "something"));
builder.RegisterType<DoesSomethingElse>()
.As<IDoesSomethingElse>()
.WithParameter("helper", new HelperClass("do", "somethingelse"));
насколько я могу судить, нет необходимости в интерфейсе для HelperClass
потому что это по существу просто держатель стоимости.
для этого вам нужно будет сделать внутренний конструктор общедоступным, я думаю.
существует два способа передачи параметров в Autofac:
при регистрации компонентов, у вас есть возможность предоставить ряд параметров, которые могут быть использованы при разрешении служб на основе этого компонента. Autofac предлагает различные стратегии подбора параметра:
-
NamedParameter
- сопоставьте целевые параметры по имени -
TypedParameter
- совпадение целевых параметров по типу (точное совпадение типа обязательно) -
ResolvedParameter
- гибкий параметра, соответствующего// 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
иTypedParameter
может поставлять только постоянные значения.ResolvedParameter
может использоваться как способ предоставления значений, динамически извлекаемых из контейнера, например, путем разрешения службы по имени.
в случае, если вы хотите передать в качестве параметра служба, которая уже зарегистрирована, например, IConfiguration
, вы можете разрешите параметр, как показано ниже:
builder.RegisterType<Service>()
.As<Iervice>()
.WithParameter((pi, ctx) => pi.ParameterType == typeof(IConfiguration) && pi.Name == "configuration",
(pi, ctx) => ctx.Resolve<IConfiguration>());
один из способов передать параметр во время выполнения в Autofac-использовать Resolve
метод. Вы можете создать такой класс:
public class ContainerManager
{
public IContainer Container {get;set;}
//...
public T[] ResolveAllWithParameters<T>(IEnumerable<Parameter> parameters)
{
return Container.Resolve<IEnumerable<T>>(parameters).ToArray();
}
}
Parameter
является абстрактным классом, который принадлежит Autofac, вы можете использовать NamedParameter
класс для передачи параметров, которые вам нужны. Вы можете использовать ContainerManager
класс, как я показываю ниже:
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);
}
таким образом, вы можете передать параметры во время выполнения с помощью Dictionary<string, object>
при разрешении конкретного компонента.
С помощью Метод Расширения может быть еще проще:
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();
}
}