Подтвердить что ты не робот

Передача параметров конструкторам с использованием 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?
4b9b3361

Ответ 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();
    }
}