【问题标题】:Instantiation of a class that uses IOption使用 IOption 的类的实例化
【发布时间】:2019-02-09 12:15:17
【问题描述】:

在 ASP.NET Core 2.1 中,我有一个使用 IOptions 接口发送电子邮件的类,如下所示:

public class Email
{
    private readonly ManuelaIbiEmail manuelaIbiEmail;

    public Email(IOptions<ManuelaIbiEmail> manuelaIbiEmail)
    {
        this.manuelaIbiEmail.Username = manuelaIbiEmail.Value.Username;
        this.manuelaIbiEmail.Password = manuelaIbiEmail.Value.Password;
    }

    public async Task SendAsync(Contato contato)
    {
        var smtpClient = new SmtpClient
        {
            Host = "smtp.sendgrid.net",
            Port = 587,
            EnableSsl = true,
            Credentials = new NetworkCredential(manuelaIbiEmail.Username, manuelaIbiEmail.Password)
        };

        using (var message = new MailMessage(contato.Email, "manuelaibi66@gmail.com")
        {
            Subject = "Email de Manuela Ibi Nutrição Integrada",
            Body = $"{contato.Comentario}\nTelefone: {contato.Telefone}"
        })
        {
            await smtpClient.SendMailAsync(message);
        }
    }
}

这个类必须在控制器中用类似“var whatever = new Email(???);”的行来实例化为了可以调用 SendAsync 方法。

但这里的大局是我迷失在这个初始化中。我只是不知道在“Email(something)”中调用什么。

只是,提供更多信息以防万一:

系统应该从 Secrets 中读取用户名、密码,因此我在 Startup.cs 中有以下行:

services.Configure<ManuelaIbiEmail>(Configuration.GetSection("ManuelaIbiEmail"));

ManuelaIbiEmail 是一个非常简单的 POCO 类:

public class ManuelaIbiEmail
{
    public string Username { get; set; }
    public string Password { get; set; }
}

控制器看起来像:

public class ContactController : Controller
{
    private readonly ManuelaIbiEmail manuelaIbiEmail;

    public IActionResult Contact()
    {
        return View(new Contato());
    }

    [HttpPost]
    public async Task<IActionResult> ContactAsync(Contato contato)
    {
        var email = new Email();
        email.SendAsync(contato);

        return Ok();
    }
}

如果有人能告诉我我缺少什么,我将不胜感激。

提前谢谢你

【问题讨论】:

  • 如果你调用new Email(...)那么你做错了DI,注入那个类以及IOptions
  • 不要将IOptions&lt;ManuelaIbiEmail&gt; 注入Email。相反,将ManuelaIbiEmail 直接注入Email,如here 所述。

标签: c# asp.net-mvc asp.net-core dependency-injection .net-core


【解决方案1】:

依赖注入的全部意义在于不要那样做

您可以通过属性将其注入方法中:

public class ContactController : Controller
{
    public IActionResult Contact()
    {
        return View(new Contato());
    }

    [HttpPost]
    public async Task<IActionResult> ContactAsync(Contato contato, [FromServices]Email email)
    {
        email.SendAsync(contato);

        return Ok();
    }
}

或者通过构造函数注入:

public class ContactController : Controller
{
    private readonly EMail email;

    public ContactController(Email email)
    {
        this.email = email;
    }

    public IActionResult Contact()
    {
        return View(new Contato());
    }

    [HttpPost]
    public async Task<IActionResult> ContactAsync(Contato contato)
    {
        email.SendAsync(contato);

        return Ok();
    }
}

【讨论】:

    【解决方案2】:

    只需通过构造函数注入Email

    public class ContactController : Controller
    {
        public ContactController(Email email)
        {
            Email = email;
        }
    
        public Email Email { get; }
    
        public IActionResult Contact()
        {
            return View(new Contato());
        }
    
        [HttpPost]
        public async Task<IActionResult> ContactAsync(Contato contato)
        {
            Email.SendAsync(contato);
    
            return Ok();
        }
    }
    

    【讨论】:

    • 最重要的是,不要将IOptions&lt;ManuelaIbiEmail&gt; 注入Email。相反,将ManuelaIbiEmail 直接注入Email
    【解决方案3】:

    理想情况下,您不希望通过手动初始化Email 将控制器与实现问题紧密耦合,因为类应该依赖于抽象而不是具体

    抽象电子邮件类

    public interface IEmail {
        Task SendAsync(Contato contato);
    }
    
    public class Email: IEmail {
        //...omitted for brevity
    }
    

    您可以通过构造函数注入将其注入控制器

    public class ContactController : Controller {
        private readonly IEmail email;
    
        public ContactController(IEmail email) {
            this.email = email;
        }
    
        public IActionResult Contact() {
            return View(new Contato());
        }
    
        [HttpPost]
        public async Task<IActionResult> ContactAsync(Contato contato) {        
            await email.SendAsync(contato);
            return Ok();
        }
    }
    

    一旦配置正确

    services.AddTransient<IEmail, Email>();
    

    容器将在创建注入对象图时解析所有依赖项。

    【讨论】:

      【解决方案4】:

      我的提议:

      Create IOption 实例并使用此实例初始化您的类电子邮件

      using Microsoft.Extensions.Options;
      
      var mail = new ManuelaIbiEmail() { Username= "", Password="" };
      IOptions<ManuelaIbiEmail> options = Options.Create(mail);
      var email = new Email(options);
      

      【讨论】:

      • 如果我的一个开发人员向我展示了这段代码,我会给他们一个耳光,让他们正确地去做(就像其他答案一样)
      • 这可能不在生产代码中。但是,它可能对单元测试场景很有帮助。如果你给出你的答案,人们可​​能不会反对它。这是有效的信息,这不是一个人通常应该做的。
      • 我试图提供正确的信息。尽管它喜欢诡计和肮脏的解决方案,但它仍然可以帮助解决这个问题并在某些方面有所帮助。但是你可以随心所欲地投票,每个人都有意见。我的回答有他的价值,我不删:)
      • @nvoigt 但是这个问题并不要求单元测试帮助,所以即使有这个旋转我也不会发现这个答案是“好”
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多