C# 使用 Autofac 将参数传递给构造函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9066200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Passing parameters to constructors using Autofac
提问by Paul
I'm very new to autofac so it's possible that I'm completely misusing it.
我对 autofac 很陌生,所以我可能完全误用了它。
Let's say I have a class that has this structure:
假设我有一个具有这种结构的类:
public class HelperClass : IHelperClass
{
public HelperClass(string a, string b)
{
this.A = a;
this.B = b;
}
}
and I have two classes that use that class, but require different defaults for the constructor. The second constructor is JUST for testing purposes -- we will always want a HelperClass in the "real" app.:
我有两个使用该类的类,但构造函数需要不同的默认值。第二个构造函数仅用于测试目的——我们总是希望在“真实”应用程序中有一个 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;
}
}
Here's my AutoFac module:
这是我的 AutoFac 模块:
public class SomethingModule: Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<DoesSomething>().As<IDoesSomething>();
builder.RegisterType<DoesSomethingElse>().As<IDoesSomethingElse();
}
}
My question(s):
我的问题:
- When I call resolve on DoesSomething or DoesSomethignElse -- will it resolve the internal constructor instead of the public one? Do I need to leave IHelperClass unregistered?
- If yes, how do I make it pass different parameters to each instance of IHelperClass depending on whether it's used in DoesSomething or DoesSomethingElse?
- 当我在DoesSomething 或DoesSomethignElse 上调用resolve 时——它会解析内部构造函数而不是公共构造函数吗?我需要让 IHelperClass 未注册吗?
- 如果是,我如何让它根据它是在DoesSomething 还是DoesSomethingElse 中使用,将不同的参数传递给IHelperClass 的每个实例?
采纳答案by Pavel Gatilov
Autofac does not use non-public constructors. By default, it only finds public ones and simply doesn't see the others. Unless you use .FindConstructorsWith(BindingFlags.NonPublic), it will see only public constructors. Therefore your scenario should work as you expect it to do.
Autofac 不使用非公共构造函数。默认情况下,它只找到公共的,根本看不到其他的。除非您使用.FindConstructorsWith(BindingFlags.NonPublic),否则它只会看到公共构造函数。因此,您的场景应该按照您的预期工作。
回答by Daniel Hilgarth
You can always use the WithParametermethod to explicitly specify a constructor parameter:
您始终可以使用该WithParameter方法显式指定构造函数参数:
builder.RegisterType<DoesSomething>()
.As<IDoesSomething>()
.WithParameter("helper", new HelperClass("do", "something"));
builder.RegisterType<DoesSomethingElse>()
.As<IDoesSomethingElse>()
.WithParameter("helper", new HelperClass("do", "somethingelse"));
As far as I can tell there is no need for an interface for HelperClassbecause it essentially is just a value holder.
据我所知,不需要接口,HelperClass因为它本质上只是一个价值持有者。
For this to work you would need to make the internal constructor public, I think.
为此,我认为您需要公开内部构造函数。
回答by octavioccl
There are two ways to pass parameters in Autofac:
Autofac中有两种传递参数的方式:
When you are registering the component:
When you register components, you have the ability to provide a set of parameters that can be used during the resolution of services based on that component. Autofac offers several different parameter matching strategies:
注册组件时,您可以提供一组参数,这些参数可在基于该组件的服务解析期间使用。Autofac 提供了几种不同的参数匹配策略:
NamedParameter- match target parameters by nameTypedParameter- match target parameters by type (exact type match required)ResolvedParameter- flexible parameter matching// 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"));NamedParameterandTypedParametercan supply constant values only.ResolvedParametercan be used as a way to supply values dynamically retrieved from the container, e.g. by resolving a service by name.
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可以用作提供从容器动态检索的值的一种方式,例如通过按名称解析服务。
In case you want to pass as parameter a service that is already registered, eg, IConfiguration, you can resolve the parameter as I show below:
如果您想将已注册的服务作为参数传递,例如IConfiguration,您可以解析参数,如下所示:
builder.RegisterType<Service>()
.As<Iervice>()
.WithParameter((pi, ctx) => pi.ParameterType == typeof(IConfiguration) && pi.Name == "configuration",
(pi, ctx) => ctx.Resolve<IConfiguration>());
When you are resolving the component:
One way to pass parameter at runtime in Autofac is using the Resolvemethod. You could create a class like this:
在 Autofac 中在运行时传递参数的Resolve一种方法是使用该方法。你可以创建一个这样的类:
public class ContainerManager
{
public IContainer Container {get;set;}
//...
public T[] ResolveAllWithParameters<T>(IEnumerable<Parameter> parameters)
{
return Container.Resolve<IEnumerable<T>>(parameters).ToArray();
}
}
Parameteris an abstract class that belongs to Autofac, you can use the NamedParameterclass to pass the parameters that you need. You can use the ContainerManagerclass as I show below:
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);
}
This way you can pass the parameters at runtime using a Dictionary<string, object>when you are resolving an specific component.
通过这种方式,您可以在Dictionary<string, object>解析特定组件时使用 a 在运行时传递参数。
Using an Extension Methodcould be even more simple:
使用扩展方法可能更简单:
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();
}
}
回答by Roland Roos
Yes, it is possible to pass only a sub set of parameters:
是的,可以只传递一组参数:
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);

