Sergio Di Fiore
Sergio Di Fiore

Reputation: 466

Instantiation of a class that uses IOption

In ASP.NET Core 2.1 I have a class to send e-mail that uses IOptions interface as shown bellow:

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, "[email protected]")
        {
            Subject = "Email de Manuela Ibi Nutrição Integrada",
            Body = $"{contato.Comentario}\nTelefone: {contato.Telefone}"
        })
        {
            await smtpClient.SendMailAsync(message);
        }
    }
}

This class has to be instantiated in the controller with a line like "var whatever = new Email(???);" in order that the SendAsync method can be called.

But the big picture here is that I'm lost in this initialization. I just don't know what to call in "Email(something)".

Just, giving a little more information in case is needed:

The system should read the username, password from Secrets and thus I have the following line in Startup.cs:

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

The ManuelaIbiEmail is a very simple POCO class:

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

The controller looks like:

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();
    }
}

If anyone can be kind enough to tell me what I am missing, I'd appreciate.

Thank you in advance

Upvotes: 3

Views: 1469

Answers (4)

Nkosi
Nkosi

Reputation: 247283

Ideally you do not want to tightly couple your controller to implementation concerns by manually initializing the Email as classes should depend on abstractions and not concretions.

Abstract the email class

public interface IEmail {
    Task SendAsync(Contato contato);
}

public class Email: IEmail {
    //...omitted for brevity
}

and you could inject it into the controller via constructor injection

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();
    }
}

Once configured correctly

services.AddTransient<IEmail, Email>();

the container will resolve all dependencies while creating the object graph for injection.

Upvotes: 1

Mark Seemann
Mark Seemann

Reputation: 233192

Just inject Email via the constructor:

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();
    }
}

Upvotes: 1

Antoine V
Antoine V

Reputation: 7204

My proposition :

Create a IOption instance and init your class Email with this instance

using Microsoft.Extensions.Options;

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

Upvotes: -1

nvoigt
nvoigt

Reputation: 77304

The whole point of dependency injection is to not do that.

You can inject it either in the method via attributes:

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();
    }
}

Or through constructor injection:

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();
    }
}

Upvotes: 4

Related Questions