Передача параметров конструкторам с использованием Autofac - PullRequest
36 голосов
/ 30 января 2012

Я очень плохо знаком с автофаком, поэтому вполне возможно, что я полностью им не пользуюсь.

Допустим, у меня есть класс с такой структурой:

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();
    }
}

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

  1. Когда я вызываю resol для DoesSomething или DoesSomethignElse - разрешит ли он внутренний конструктор вместо общедоступного? Нужно ли оставлять IHelperClass незарегистрированным?
  2. Если да, как мне сделать так, чтобы он передавал различные параметры каждому экземпляру IHelperClass в зависимости от того, используется ли он в DoesSomething или DoesSomethingElse?

Ответы [ 3 ]

52 голосов
/ 30 января 2012

Вы всегда можете использовать метод WithParameter, чтобы явно указать параметр конструктора:

builder.RegisterType<DoesSomething>()
       .As<IDoesSomething>()
       .WithParameter("helper", new HelperClass("do", "something"));

builder.RegisterType<DoesSomethingElse>()
       .As<IDoesSomethingElse>()
       .WithParameter("helper", new HelperClass("do", "somethingelse"));

Насколько я могу судить, интерфейс для HelperClass не нужен, потому что он, по сути, простодержатель значения.

Чтобы это работало, вам нужно сделать внутренний конструктор общедоступным, я думаю.

44 голосов
/ 12 июня 2013

Существует два способа передачи параметров в 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();
    }
}
10 голосов
/ 30 января 2012

Autofac не использует закрытые конструкторы.По умолчанию он только находит общедоступные и просто не видит остальных.Если вы не используете .FindConstructorsWith(BindingFlags.NonPublic), он будет видеть только публичные конструкторы.Поэтому ваш сценарий должен работать так, как вы ожидаете.

...