使用Autofac将参数传递给构造函数

本文关键字:构造函数 参数传递 Autofac 使用 | 更新日期: 2023-09-27 18:21:58

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

我的问题:

  1. 当我在DoesSomething或DoesSomethignElse上调用resolve时,它会解析内部构造函数而不是公共构造函数吗?我需要不注册IHelperClass吗
  2. 如果是,我如何使它根据DoesSomething还是DoesSometingElse中使用的IHelperClass的每个实例传递不同的参数

使用Autofac将参数传递给构造函数

您可以始终使用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"));
    

    CCD_ 6和CCD_。

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

Autofac不使用非公共构造函数。默认情况下,它只找到公共的,根本看不到其他的。除非使用.FindConstructorsWith(BindingFlags.NonPublic),否则它只能看到公共构造函数。因此,您的场景应该按照您期望的方式工作。

是的,可以只传递一个子参数集:

public Contract(IPerson person, String name)
{ 
    this.Person = person;
    person.Name = name;
}

// this uses the person/name ctor. Person is factored and injected by the contianer
List<Parameter> parameters = new List<Parameter>();
parameters.Add(new NamedParameter("name", "cloe"));
contract = scope.Resolve<IContract>(parameters);

如果您只想在Autofac容器构建后将参数传递给任何已解析组件的构造函数,请尝试委托工厂,其行为与@ocuenca的答案中的方法ResolveAllWithParameters()类似,但使用更方便,编写更少:

public class HelperClass
{
    // Note both the TYPES and NAMES in the delegate match
    // the parameters in the constructor. This is important!
    public delegate HelperClass New(string a, string b = default);
    public HelperClass(string a, string b) { }
}
builder.RegisterType<HelperClass>();
builder.RegisterType<DoesSomething>();
public class DoesSomething
{
    private readonly HelperClass _helper;
    public DoesSomething(HelperClass.New helperFactory)
    {
        _helper = helperFactory("value_of_a_param");
    }
}