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

Pau*_*aul 40 c# autofac

我对autofac很新,所以我可能会完全滥用它.

假设我有一个具有这种结构的类:

public class HelperClass : IHelperClass
{
     public HelperClass(string a, string b)
     {
         this.A = a;
         this.B = b;
     }
}
Run Code Online (Sandbox Code Playgroud)

我有两个使用该类的类,但构造函数需要不同的默认值.第二个构造函数仅用于测试目的 - 我们总是希望在"真实"应用程序中使用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;
     }
}
Run Code Online (Sandbox Code Playgroud)

这是我的AutoFac模块:

public class SomethingModule: Module
{
    protected override void Load(ContainerBuilder builder)
    {
         builder.RegisterType<DoesSomething>().As<IDoesSomething>();
         builder.RegisterType<DoesSomethingElse>().As<IDoesSomethingElse();
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题:

  1. 当我在DoesSomething或DoesSomethignElse上调用resolve时 - 它会解析内部构造函数而不是公共构造函数吗?我是否需要保留IHelperClass未注册?
  2. 如果是,我如何让它将不同的参数传递给IHelperClass的每个实例,具体取决于它是在DoesSomething还是DoesSomethingElse中使用?

Dan*_*rth 57

您始终可以使用该WithParameter方法显式指定构造函数参数:

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

builder.RegisterType<DoesSomethingElse>()
       .As<IDoesSomethingElse>()
       .WithParameter("helper", new HelperClass("do", "somethingelse"));
Run Code Online (Sandbox Code Playgroud)

据我所知,不需要接口,HelperClass因为它本质上只是一个价值持有者.

为此,你需要将内部构造函数设置为public.


oct*_*ccl 44

有两种方法可以在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"));
    
    Run Code Online (Sandbox Code Playgroud)

    NamedParameter并且TypedParameter只能提供常数值.

    ResolvedParameter 可以用作提供从容器动态检索的值的方法,例如通过按名称解析服务.

如果您想要将已注册的服务作为参数传递,例如IConfiguration,您可以解析参数,如下所示:

    builder.RegisterType<Service>()
           .As<Iervice>()
           .WithParameter((pi, ctx) => pi.ParameterType == typeof(IConfiguration) && pi.Name == "configuration",
                          (pi, ctx) => ctx.Resolve<IConfiguration>());
Run Code Online (Sandbox Code Playgroud)

在解析组件时:

在Autofac中在运行时传递参数的Resolve一种方法是使用该方法.你可以创建一个这样的类:

public class ContainerManager
{
  public IContainer Container {get;set;}
  //...
  public T[] ResolveAllWithParameters<T>(IEnumerable<Parameter> parameters)
  {
    return Container.Resolve<IEnumerable<T>>(parameters).ToArray();
  }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
Run Code Online (Sandbox Code Playgroud)

这样,您可以使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Pav*_*lov 9

Autofac不使用非公共构造函数.默认情况下,它只查找公共的,而不会看到其他的.除非你使用.FindConstructorsWith(BindingFlags.NonPublic),否则它只会看到公共构造函数.因此,您的方案应该按照您的预期工作.

  • @FrankGorman(或者其他大多数人都在寻找这个答案).你可以从这里添加一个`IContructorFinder`:https://github.com/mthamil/TFSTestCaseAutomator/blob/master/TestCaseAutomator/Container/Support/NonPublicConstructorFinder.cs (3认同)