Передача параметров конструкторам с использованием Autofac
Я очень новичок в autofac, поэтому возможно, что я полностью злоупотребляю им.
Скажем, у меня есть класс, который имеет такую структуру:
public class HelperClass : IHelperClass
{
public HelperClass(string a, string b)
{
this.A = a;
this.B = b;
}
}
и у меня есть два класса, которые используют этот класс, но для разных конструкторов требуются разные значения по умолчанию. Второй конструктор JUST предназначен для тестирования - мы всегда хотим использовать 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();
}
}
Мой вопрос (ы):
- Когда я вызываю решение на DoesSomething или DoesSomethignElse - он разрешит внутренний конструктор вместо публичного? Мне нужно оставить IHelperClass незарегистрированным?
- Если да, то каким образом я могу передать ему разные параметры для каждого экземпляра IHelperClass в зависимости от того, используется ли он в DoSomething или DoesSomethingElse?
Ответы
Ответ 1
Autofac не использует непубличные конструкторы. По умолчанию он находит только публичные и просто не видит других. Если вы не используете .FindConstructorsWith(BindingFlags.NonPublic)
, он увидит только публичные конструкторы. Поэтому ваш сценарий должен работать так, как вы ожидаете.
Ответ 2
Вы всегда можете использовать метод WithParameter
для явного указания параметра конструктора:
builder.RegisterType<DoesSomething>()
.As<IDoesSomething>()
.WithParameter("helper", new HelperClass("do", "something"));
builder.RegisterType<DoesSomethingElse>()
.As<IDoesSomethingElse>()
.WithParameter("helper", new HelperClass("do", "somethingelse"));
Насколько я могу судить, нет необходимости в интерфейсе для HelperClass
, потому что он по существу является только держателем значения.
Для этого вам нужно сделать внутренний конструктор общедоступным, я думаю.
Ответ 3
Есть два способа передать параметры в Autofac:
Когда вы регистрируете компонент:
При регистрации компонентов вы можете предоставить набор параметров, которые могут использоваться во время разрешения служб на основе этого компонента. Autofac предлагает несколько стратегий сопоставления параметров:
-
NamedParameter
- сопоставить целевые параметры по имени
-
TypedParameter
- сопоставление целевых параметров по типу (точное соответствие типа
требуется)
-
ResolvedParameter
- гибкое сопоставление параметров
// Using a NAMED parameter:
builder.RegisterType<ConfigReader>()
.As<IConfigReader>()
.WithParameter("configSectionName", "sectionName");// parameter name, parameter value. It 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
может использоваться как способ подачи значений, динамически извлекаемых из контейнера, например. путем разрешения службы по имени.
Когда вы разрешаете компонент:
Один из способов передать параметр во время выполнения в 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();
}
}