Skip to content

Sending Mails In Aspnetcore

This post is part of series starting here.

Here’s how I configure my mailers in a ASP.Net Core project developed in F#. I define an interface:

type IMailer =
    // to * subject * body
    abstract Send: string * string * string -> unit

of which I have 2 implementation. The first one is for development as it simply prints the mail on the server’s standard output:

type ConsoleMailer() =
    interface IMailer with
        member _.Send(recipient, subject, body) =
            printfn "Sending mail:\nTo:%s\nSubject:%s\n%s" recipient subject body

The second implementation is for production:

  open MailKit.Net.Smtp
  open MailKit
  open MimeKit

  type ProductionMailer(host:string, port:int, login:string,password:string) =
    interface IMailer with
        member _.Send(recipient, subject, body) =
            let message = new MimeMessage()

            message.From.Add (new MailboxAddress ("ProjectName", "info@example.com"))
            message.To.Add (new MailboxAddress ("", recipient));
            message.Subject <- subject
            message.Body <- new TextPart("plain",Text=body)
            try
                let client = new SmtpClient()
                client.Connect(host,port,true)
                client.Authenticate(login,password)
                client.Send(message)|> ignore
                client.Disconnect(true)
            with e ->
                printfn "Exception: %A" e

and its config is set in a JSON file handled by this code:

open FSharp.Data
type MailConfig = JsonProvider<"config/mail.json.sample", EmbeddedResource="lib, lib.embedded.mail.json.sample">

It uses a type provider using the project’s provided sample config file, which is

{
    "host":"mail.example.com",
    "port": 25,
    "login": "username",
    "password": "superpass"
}

With all building block available, we can then inject them in the application in the Startup class' ConfigureServices method.

       if env.IsDevelopment() then
            services.AddTransient<IMailer,ConsoleMailer>() |> ignore
        else
            let mailConfigFile = Environment.GetEnvironmentVariable("WADESDA_MAIL_CONFIG_FILE")
            if not (isNull mailConfigFile) && System.IO.File.Exists(mailConfigFile) then
                let mailConfig = MailConfig.Load(mailConfigFile)
                let mailer =
                    ProductionMailer(mailConfig.Host,
                                     mailConfig.Port,
                                     mailConfig.Login,
                                     mailConfig.Password
                    )
                services.AddTransient<IMailer>(fun s ->  mailer :> IMailer) |> ignore

We inject the dependency as transient so that every request for an instance gets a new one. And that’s it. In the app, you fist need to get a handle on the mailer. In WebSharper, you do it like this:

        let httpContext:Microsoft.AspNetCore.Http.HttpContext = ctx.HttpContext()
        let serviceProvider= httpContext.RequestServices
        let mailer = serviceProvider.GetService(typeof<IMailer>)

Sending a mail is then done like this:

mailer.Send(emailAddress,subject, body)