Передача параметров конструкторам с помощью 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();
    }
}

мой вопрос(ы):

  1. когда я вызываю resolve на DoesSomething или DoesSomethignElse -- будет ли он разрешать внутренний конструктор вместо общедоступного? Нужно ли оставлять IHelperClass незарегистрированным?
  2. если да, то как заставить его передавать разные параметры каждому экземпляру 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();
    }
}