22

In Asp.net, I can normally send emails using the following code:

using (var smtp = new SmtpClient())
{
    await smtp.SendMailAsync(mailMessage);
}

With the smtp settings being provided in the web.config, which are then automatically used by the SmtpClient. The web.config config section looks like:

<mailSettings>
    <smtp deliveryMethod="Network">
      <network host="myHost" port="25" userName="myUsername" password="myPassword" defaultCredentials="false" />
    </smtp>
</mailSettings>

Is it possible to have config in the appSettings.json file in a dotnet core 2.0 application, which can then be used by the SmtpClient, similar to Asp.net?

ViqMontana
  • 5,090
  • 3
  • 19
  • 54
  • Have a look at https://stackoverflow.com/questions/41407221/startup-cs-in-a-self-hosted-net-core-console-application this should answer your question – Daniel Feb 21 '19 at 10:00
  • 1
    First, don't use SmptClient. Second, there's no special meaning to any XML or JSON section with .NET Core. There's no special meaning to `appsettings.json` either, it's just the default file name used by WebHostBuilder. – Panagiotis Kanavos Feb 21 '19 at 10:01
  • So use what instead of `SmptClient`? And so you're saying I'll have to specify the smtp details manually? – ViqMontana Feb 21 '19 at 10:02
  • 3
    To explain `don't use SmptClient.`, the class's documentation itself explains [it's obsolete](https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient?view=netframework-4.7.2), only there for compatibility reasons *even in the Full Framework* and that you [should use `MailKit` instead](https://github.com/dotnet/platform-compat/blob/master/docs/DE0005.md) – Panagiotis Kanavos Feb 21 '19 at 10:02
  • 1
    The *proper* way to use whatever SMTP service you want is to load its configuration from the .NET Configuration system, from whatever source you've configured. It could be a section named `"MyMailSettings"` in your `json` file. Second, you should *register* that class as a service, not instantiate it manually. – Panagiotis Kanavos Feb 21 '19 at 10:04
  • @PanagiotisKanavos I am using DI to inject my email service. Thank you for your help, I'm going to go along with [this](https://dotnetcoretutorials.com/2017/11/02/using-mailkit-send-receive-email-asp-net-core/) example. – ViqMontana Feb 21 '19 at 10:06
  • 3
    Check [Using MailKit To Send And Receive Email In ASP.net Core](https://dotnetcoretutorials.com/2017/11/02/using-mailkit-send-receive-email-asp-net-core/). It shows how you can load settings from JSON, how to create and register an `IEmailService` – Panagiotis Kanavos Feb 21 '19 at 10:07
  • Can I pass the SmtpClient via dependency injection rather than new'ing it up? – variable Jan 13 '22 at 14:36
  • The above link is using mailkit but it still calls SmtpClient? – variable Jan 13 '22 at 14:38

2 Answers2

20

If you insist on using System.Net.Mail.SmtpClient, you can do it this way:

appsettings.json

{
  "Smtp": {
    "Server": "mail.whatever.com",
    "Port": 25,
    "FromAddress": "yourfromemail@whatever.com"
  },
}

Code:

    public async Task SendEmailAsync(string email, string subject, string htmlMessage)
    {
        MailMessage message = new MailMessage();
        message.Subject = subject;
        message.Body = htmlMessage;
        message.IsBodyHtml = true;
        message.To.Add(email);

        string host = _config.GetValue<string>("Smtp:Server", "defaultmailserver");
        int port = _config.GetValue<int>("Smtp:Port", 25);
        string fromAddress = _config.GetValue<string>("Smtp:FromAddress", "defaultfromaddress");

        message.From = new MailAddress(fromAddress);

        using (var smtpClient = new SmtpClient(host, port))
        {
            await smtpClient.SendMailAsync(message);
        }
    }

Where _config is an implementation of IConfiguration which is injected into the class in which the SendEmailAsync method resides.

However, since it's obsolete, it might be better to explore other methods as mentioned in the comments above.

HaukurHaf
  • 13,522
  • 5
  • 44
  • 59
  • 4
    I don't want to manually get the smtp config values from appsettings.json though. I was asking whether the values can be used automatically. – ViqMontana Feb 21 '19 at 10:15
  • Don't think that's possible. – HaukurHaf Feb 21 '19 at 10:31
  • It gives me this error "Mailbox unavailable. The server response was: Unauthenticated senders not allowed" @HaukurHaf – Nitika Chopra Feb 22 '20 at 06:47
  • @NitikaChopra that means that the mail server you are using requires authentication. You need to add support for username/passwords and then send that information into the SmtpClient class constructor along with the host and port. – HaukurHaf Feb 24 '20 at 08:38
  • @HaukurHaf thanks for reply. but actually my silly problem is that I entered the wrong username, I rectify this my problem is solved.. :) – Nitika Chopra Feb 24 '20 at 09:16
  • Can I pass the SmtpClient via dependency injection rather than new'ing it up? – variable Jan 13 '22 at 14:35
  • @variable I can't see why not ... – HaukurHaf Jan 14 '22 at 10:47
  • Ok so in that case am I correct to say that the injected SmtpClient and its parent service class (if any) must be both registered as transient or scoped and not singleton? – variable Jan 14 '22 at 10:49
  • Regular DI lifetime restrictions/best practices should apply. Sorry for the vague answer :) – HaukurHaf Jan 14 '22 at 11:17
9

What you probably want to achieve is like this:

File: appsettings.json (where you define your data)

{
    ...
    "EmailSettings": {
        "From": "no-reply@example.com",
        "Username": "username",
        "Password": "password",
        "Host": "smtp.example.com",
        "Port": 25
    },
    ...
}

File: Email.cs (where you define a model that will contain the data)

public class EmailSettings
{
    public string From { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public string Host { get; set; }
    public int Port { get; set; }
}

File: Startup.cs (where you bind the data to the model that will be available everywhere)

public void ConfigureServices(IServiceCollection services)
{              
   ...

     services.AddConfiguration<EmailSettings>(Configuration, "EmailSettings");

   ...
}

File: Email.cs (where you define how to send a mail)

public class EmailService
{
    private readonly EmailSettings _mailSettings;
    public EmailService(EmailSettings mailSettings)
    {
        _mailSettings = mailSettings;
    }

    public async Task<bool> SendMail(MailRequest mailRequest)
    {
        //Here goes your code to send a message using "_mailSettings" like:
        // _mailSettings.From
        // _mailSettings.Port
        //ect...
    }
}

To do so open this tutorial and go to Way 6 named "#6 AppSettings – PRE-Binding"

There your will find different ways to make this work but the 6th way is highly recommended

Alex Logvin
  • 766
  • 10
  • 12
  • 1
    I took this answer and went further. Using the IOptions interface to implement Alex's suggestion inside a .Net Core 6 Program.cs. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-6.0 – Robini Jan 28 '22 at 15:05