简体   繁体   English

在Autofac“属性注入”/ ASP.NET Webforms应用程序中解析相同类型的2个属性

[英]Resolve 2 properties of the same type in Autofac “property injection” / ASP.NET Webforms Application

I have a BasePage class with two properties of the same dependency type and I couldn't find a way to provide the required parameters using Autofac 我有一个具有相同依赖类型的两个属性的BasePage类,我找不到使用Autofac提供所需参数的方法

I tried to register the types and both BasePage properties are pointing now to the same last registered component wich is FaxSender, I added a breakpoint at the function TryGetDeclaringProperty and it works fine and checking the names of properties of the BasePage class. 我尝试注册类型,并且两个BasePage属性现在指向与FaxSender相同的最后一个注册组件,我在函数TryGetDeclaringProperty添加了一个断点,它工作正常并检查BasePage类的属性名称。

[UPDATED Example] [更新示例]

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)
    {
        //...
    }
}

in Global.asax.cs 在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>();
        }

in Default.aspx.cs 在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(...));
}

Due to the architecture of ASP.net webform, Page instances are not created by Autofac you can't configure any registration for the page instance. 由于ASP.net webform的体系结构, Page实例不是由Autofac创建的, 因此您无法为页面实例配置任何注册。 The WithProperty method won't be used in this case. 在这种情况下,不会使用WithProperty方法。

In your case one solution would be to manually resolve the dependency. 在您的情况下,一种解决方案是手动解决依赖关系。

In your BasePage component 在您的BasePage组件中

public ISender EmailSender
{
    get
    {
        var cpa = (IContainerProviderAccessor)this.Context.ApplicationInstance;
        return cpa.ContainerProvider.RequestLifetime.ResolveNamed<ISender>("email");
    }
}

Another solution would be to use IIndex<TKey, TValue> . 另一种解决方案是使用IIndex<TKey, TValue> Instead of having 2 properties you can have only one which contains all your ISender . 您可以只拥有一个包含所有ISender属性,而不是拥有2个属性。

public IIndex<String, ISender> Senders {get; set; } 

And when you need a specific sender you can access it through 当您需要特定的发件人时,您可以通过它访问它

ISender emailSender = this.Senders["email"]; 

By the way, if you want the expected behavior but for an instance created by Autofac you should use NamedPropertyParameter instead of NamedParameter . 顺便说一句,如果你想要预期的行为,但对于Autofac创建的实例,你应该使用NamedPropertyParameter而不是NamedParameter

// not working for webform !!! 
builder.RegisterType<BasePage>()
       .WithProperties(new Parameter[]{
           new NamedPropertyParameter("X", emailSender),
           new NamedPropertyParameter("Y", faxSender),
       });

If you want to get your values with dependency injection there is no easy way to do it. 如果你想通过依赖注入来获取你的值,那么就没有简单的方法。

Autofac rely internally to this extension method : 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;
  }
}

and you can use it with a ResolvedParameter this way : 你可以用这种方式将它与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"))
       });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM