在 Autofac "property injection" / ASP.NET Webforms Application 中解析 2 个相同类型的属性
Resolve 2 properties of the same type in Autofac "property injection" / ASP.NET Webforms Application
我有一个 BasePage class,它有两个相同依赖类型的属性,我找不到使用 Autofac
提供所需参数的方法
我尝试注册类型,两个 BasePage 属性现在都指向同一个最后注册的组件,即 FaxSender,我在函数 TryGetDeclaringProperty 添加了一个断点,它工作正常并检查 BasePage [的属性名称=29=].
[更新示例]
public class BasePage : System.Web.UI.Page
{
public ISender EmailSender { get; set; }
public ISender FaxSender { get; set; }
}
public class EmailSender : ISender
{
private readonly SmtpClient _smtpClient;
public EmailSender(SmtpClient smtpClient)
{
_smtpClient = smtpClient;
}
public void Send(INotification notification)
{
//...
}
}
public class FaxSender : ISender
{
private readonly SmtpClient _smtpClient;
public FaxSender(SmtpClient smtpClient)
{
_smtpClient = smtpClient;
}
public void Send(INotification notification)
{
//...
}
}
在Global.asax.cs
var emailSmtp = new SmtpClient
{
...
};
var emailSender = new EmailSender(emailSmtp);
var faxSmtp = new SmtpClient
{
...
};
var faxSender = new FaxSender(faxSmtp);
var builder = new ContainerBuilder();
//--------------------------------
builder.RegisterType<BasePage>()
.WithProperties(new Parameter[]{
new NamedPropertyParameter("EmailSender", emailSender),
new NamedPropertyParameter("FaxSender", faxSender),
});
//--------------------------------
//OR
//--------------------------------
builder.RegisterType<EmailSender>()
.Named<ISender>("email")
.WithParameter("smtpClient", emailSmtp);
builder.RegisterType<FaxSender>()
.Named<ISender>("fax")
.WithParameter("smtpClient", faxSmtp);
builder.RegisterType<BasePage>()
.AsSelf()
.WithProperties(new Parameter[] {
new ResolvedParameter(
(pi, c) => {
PropertyInfo ppi = null;
if (pi.TryGetDeclaringProperty(out ppi)) {
return ppi.Name == "EmailSender";
} else {
return false;
}
},
(pi, c) => c.ResolveNamed<ISender>("email")),
new ResolvedParameter(
(pi, c) => {
PropertyInfo ppi = null;
if (pi.TryGetDeclaringProperty(out ppi)) {
return ppi.Name == "FaxSender";
} else {
return false;
}
},
(pi, c) => c.ResolveNamed<ISender>("fax"))
});
//--------------------------------
//and then
var container = builder.Build();
_containerProvider = new ContainerProvider(container);
using (var scope = container.BeginLifetimeScope())
{
scope.Resolve<BasePage>();
}
在Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
//Object reference not set to an instance of an object. [Exception in both cases]
EmailSender.Send(new EmailNotification(...);
FaxSender.Send(new FaxNotification(...));
}
由于 ASP.net webform 的架构,Page
实例不是由 Autofac 创建的,您无法为页面实例配置任何注册。在这种情况下不会使用 WithProperty
方法。
在您的情况下,一种解决方案是手动解决依赖关系。
在您的 BasePage
组件中
public ISender EmailSender
{
get
{
var cpa = (IContainerProviderAccessor)this.Context.ApplicationInstance;
return cpa.ContainerProvider.RequestLifetime.ResolveNamed<ISender>("email");
}
}
另一个解决方案是使用 IIndex<TKey, TValue>
。您可以只拥有一个包含所有 ISender
的属性,而不是拥有 2 个属性。
public IIndex<String, ISender> Senders {get; set; }
当您需要特定的发件人时,您可以通过
访问它
ISender emailSender = this.Senders["email"];
顺便说一句,如果您想要预期的行为,但对于由 Autofac 创建的实例,您应该使用 NamedPropertyParameter
而不是 NamedParameter
。
// not working for webform !!!
builder.RegisterType<BasePage>()
.WithProperties(new Parameter[]{
new NamedPropertyParameter("X", emailSender),
new NamedPropertyParameter("Y", faxSender),
});
如果你想通过依赖注入获取你的值,没有简单的方法可以做到。
Autofac 内部依赖于此扩展方法:
public static class ParameterInfoExtensions
{
public static bool TryGetDeclaringProperty(this ParameterInfo pi, out PropertyInfo prop)
{
MethodInfo mi = pi.Member as MethodInfo;
if (mi != (MethodInfo)null && mi.IsSpecialName
&& mi.Name.StartsWith("set_", StringComparison.Ordinal)
&& mi.DeclaringType != (Type)null)
{
prop = mi.DeclaringType.GetTypeInfo().GetDeclaredProperty(mi.Name.Substring(4));
return true;
}
prop = null;
return false;
}
}
并且您可以通过这种方式将其与 ResolvedParameter
一起使用:
builder.RegisterType<EmailSender>()
.Named<ISender>("email")
.WithParameter("smtpClient", emailSmtp);
builder.RegisterType<FaxSender>()
.Named<ISender>("fax")
.WithParameter("smtpClient", faxSmtp);
builder.RegisterType<BasePage>()
.AsSelf()
.WithProperties(new Parameter[] {
new ResolvedParameter(
(pi, c) => {
PropertyInfo ppi = null;
if (pi.TryGetDeclaringProperty(out ppi)) {
return ppi.Name == "SmtpClient";
} else {
return false;
}
},
(pi, c) => c.ResolveNamed<ISender>("email")),
new ResolvedParameter(
(pi, c) => {
PropertyInfo ppi = null;
if (pi.TryGetDeclaringProperty(out ppi)) {
return ppi.Name == "FaxClient";
} else {
return false;
}
},
(pi, c) => c.ResolveNamed<ISender>("fax"))
});
我有一个 BasePage class,它有两个相同依赖类型的属性,我找不到使用 Autofac
提供所需参数的方法我尝试注册类型,两个 BasePage 属性现在都指向同一个最后注册的组件,即 FaxSender,我在函数 TryGetDeclaringProperty 添加了一个断点,它工作正常并检查 BasePage [的属性名称=29=].
[更新示例]
public class BasePage : System.Web.UI.Page
{
public ISender EmailSender { get; set; }
public ISender FaxSender { get; set; }
}
public class EmailSender : ISender
{
private readonly SmtpClient _smtpClient;
public EmailSender(SmtpClient smtpClient)
{
_smtpClient = smtpClient;
}
public void Send(INotification notification)
{
//...
}
}
public class FaxSender : ISender
{
private readonly SmtpClient _smtpClient;
public FaxSender(SmtpClient smtpClient)
{
_smtpClient = smtpClient;
}
public void Send(INotification notification)
{
//...
}
}
在Global.asax.cs
var emailSmtp = new SmtpClient
{
...
};
var emailSender = new EmailSender(emailSmtp);
var faxSmtp = new SmtpClient
{
...
};
var faxSender = new FaxSender(faxSmtp);
var builder = new ContainerBuilder();
//--------------------------------
builder.RegisterType<BasePage>()
.WithProperties(new Parameter[]{
new NamedPropertyParameter("EmailSender", emailSender),
new NamedPropertyParameter("FaxSender", faxSender),
});
//--------------------------------
//OR
//--------------------------------
builder.RegisterType<EmailSender>()
.Named<ISender>("email")
.WithParameter("smtpClient", emailSmtp);
builder.RegisterType<FaxSender>()
.Named<ISender>("fax")
.WithParameter("smtpClient", faxSmtp);
builder.RegisterType<BasePage>()
.AsSelf()
.WithProperties(new Parameter[] {
new ResolvedParameter(
(pi, c) => {
PropertyInfo ppi = null;
if (pi.TryGetDeclaringProperty(out ppi)) {
return ppi.Name == "EmailSender";
} else {
return false;
}
},
(pi, c) => c.ResolveNamed<ISender>("email")),
new ResolvedParameter(
(pi, c) => {
PropertyInfo ppi = null;
if (pi.TryGetDeclaringProperty(out ppi)) {
return ppi.Name == "FaxSender";
} else {
return false;
}
},
(pi, c) => c.ResolveNamed<ISender>("fax"))
});
//--------------------------------
//and then
var container = builder.Build();
_containerProvider = new ContainerProvider(container);
using (var scope = container.BeginLifetimeScope())
{
scope.Resolve<BasePage>();
}
在Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
//Object reference not set to an instance of an object. [Exception in both cases]
EmailSender.Send(new EmailNotification(...);
FaxSender.Send(new FaxNotification(...));
}
由于 ASP.net webform 的架构,Page
实例不是由 Autofac 创建的,您无法为页面实例配置任何注册。在这种情况下不会使用 WithProperty
方法。
在您的情况下,一种解决方案是手动解决依赖关系。
在您的 BasePage
组件中
public ISender EmailSender
{
get
{
var cpa = (IContainerProviderAccessor)this.Context.ApplicationInstance;
return cpa.ContainerProvider.RequestLifetime.ResolveNamed<ISender>("email");
}
}
另一个解决方案是使用 IIndex<TKey, TValue>
。您可以只拥有一个包含所有 ISender
的属性,而不是拥有 2 个属性。
public IIndex<String, ISender> Senders {get; set; }
当您需要特定的发件人时,您可以通过
访问它ISender emailSender = this.Senders["email"];
顺便说一句,如果您想要预期的行为,但对于由 Autofac 创建的实例,您应该使用 NamedPropertyParameter
而不是 NamedParameter
。
// not working for webform !!!
builder.RegisterType<BasePage>()
.WithProperties(new Parameter[]{
new NamedPropertyParameter("X", emailSender),
new NamedPropertyParameter("Y", faxSender),
});
如果你想通过依赖注入获取你的值,没有简单的方法可以做到。
Autofac 内部依赖于此扩展方法:
public static class ParameterInfoExtensions
{
public static bool TryGetDeclaringProperty(this ParameterInfo pi, out PropertyInfo prop)
{
MethodInfo mi = pi.Member as MethodInfo;
if (mi != (MethodInfo)null && mi.IsSpecialName
&& mi.Name.StartsWith("set_", StringComparison.Ordinal)
&& mi.DeclaringType != (Type)null)
{
prop = mi.DeclaringType.GetTypeInfo().GetDeclaredProperty(mi.Name.Substring(4));
return true;
}
prop = null;
return false;
}
}
并且您可以通过这种方式将其与 ResolvedParameter
一起使用:
builder.RegisterType<EmailSender>()
.Named<ISender>("email")
.WithParameter("smtpClient", emailSmtp);
builder.RegisterType<FaxSender>()
.Named<ISender>("fax")
.WithParameter("smtpClient", faxSmtp);
builder.RegisterType<BasePage>()
.AsSelf()
.WithProperties(new Parameter[] {
new ResolvedParameter(
(pi, c) => {
PropertyInfo ppi = null;
if (pi.TryGetDeclaringProperty(out ppi)) {
return ppi.Name == "SmtpClient";
} else {
return false;
}
},
(pi, c) => c.ResolveNamed<ISender>("email")),
new ResolvedParameter(
(pi, c) => {
PropertyInfo ppi = null;
if (pi.TryGetDeclaringProperty(out ppi)) {
return ppi.Name == "FaxClient";
} else {
return false;
}
},
(pi, c) => c.ResolveNamed<ISender>("fax"))
});