Ozakboy.Mail 1.2.0

dotnet add package Ozakboy.Mail --version 1.2.0
                    
NuGet\Install-Package Ozakboy.Mail -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Ozakboy.Mail" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ozakboy.Mail" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Ozakboy.Mail" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Ozakboy.Mail --version 1.2.0
                    
#r "nuget: Ozakboy.Mail, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Ozakboy.Mail@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Ozakboy.Mail&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Ozakboy.Mail&version=1.2.0
                    
Install as a Cake Tool

<img src="https://raw.githubusercontent.com/ozakboy/ozakboy.Mail/main/logo.png" width="112" align="right" alt="Ozakboy.Mail" />

Ozakboy.Mail

One configuration section, one call, and the mail is out the door.

English | 繁體中文 · Changelog · API Reference · NuGet

Notification mails. Nightly report mails. Verification mails. The kind of email an application sends, not a person. Ozakboy.Mail exists so that sending one takes an SmtpMailConfig section in appsettings.json and a single method call — no SMTP client lifetime to manage, no MIME tree to assemble, no TLS mode to guess.

✉  · · ·  ✈

Send an email

using Ozakboy.Mail;

IMail mail = new Mail(configuration);

var receivers = new List<MailInfo> { new MailInfo("someone@example.com", "Someone") };

mail.SendMail(
    receivers,
    null,                                       // CC
    null,                                       // attachments
    "Your nightly report is ready",
    "<p>The report finished at 03:00.</p>");

The same thing, asynchronously, with cancellation:

IMailAsync mail = new Mail(configuration);

await mail.SendMailAsync(
    receivers,
    null,
    null,
    "Your nightly report is ready",
    "<p>The report finished at 03:00.</p>",
    true,
    cancellationToken);

With CC and an attachment:

var cc = new List<MailInfo> { new MailInfo("manager@example.com", "Manager") };

var attachments = new List<AttachmentsInfo>
{
    new AttachmentsInfo
    {
        FileName = "report.pdf",
        FileType = MediaTypeNames.Application.Pdf,
        FileStream = File.OpenRead("report.pdf"),
    },
};

await mail.SendMailAsync(receivers, cc, attachments, "Monthly report", "<p>Attached.</p>");

The attachment streams are disposed for you once the send succeeds — see the stream ownership contract.

What this is

A small SMTP sending library with a deliberately narrow surface:

  • SendMail / SendMailAsync — multiple recipients, CC, attachments, HTML or plain-text bodies
  • Configuration from appsettings.json, or straight from a VSmtpMailConfig object when your settings come from environment variables
  • Two interfaces so you can inject either style: IMail (sync) and IMailAsync (async)

And what it is not: no BCC, no templating engine, no queueing or retry layer, no mail-merge, no IMAP/POP. If you need those, use MailKit directly — that is what this library is built on.

Why it is different

MailKit underneath, not System.Net.Mail. The .NET built-in SmtpClient only speaks STARTTLS, so port 465 (implicit SSL) simply never worked — a classic afternoon lost to "my settings are right but nothing sends". Since 1.1.0 the transport is MailKit with SecureSocketOptions.Auto, which negotiates the right mode for 465, 587 and 25 without you telling it which.

Errors that tell you where to look. A failed send does not surface a raw socket error. It throws InvalidOperationException with the four things actually worth checking — network, host/port, firewall, credentials — with your configured host and port interpolated, and the original exception preserved as InnerException for your logs.

No learning curve to pay off. MailKit is excellent and correspondingly large: SmtpClient, MimeMessage, BodyBuilder, MimePart, ContentType, SecureSocketOptions. To send a notification mail you should not have to learn any of them. Here the vocabulary is MailInfo, AttachmentsInfo, and one method.

Sync is really sync. The synchronous path calls MailKit's synchronous API. There is no .Result or .Wait() hidden inside waiting to deadlock your UI or ASP.NET context.

Install and send your first mail

dotnet add package Ozakboy.Mail

1. Add the SMTP section to appsettings.json:

{
  "SmtpMailConfig": {
    "Host": "smtp.gmail.com",
    "Port": 587,
    "UserName": "your-email@gmail.com",
    "Password": "your-app-password",
    "Name": "Sender display name"
  }
}

Gmail requires an app password, not your account password. Port 587 (STARTTLS) and 465 (implicit SSL) both work.

Need the sender address to differ from the login account (AWS SES, unauthenticated relays)? Add the optional "SenderAddress" field — new in 1.2.0. See Configuration.

2. Register it (ASP.NET Core):

builder.Services.AddScoped<IMail, Mail>();
builder.Services.AddScoped<IMailAsync, Mail>();

Or build it by hand in a console app:

var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

IMail mail = new Mail(configuration);

Settings living in environment variables instead? Skip IConfiguration entirely:

IMailAsync mail = new Mail(new VSmtpMailConfig
{
    Host = Environment.GetEnvironmentVariable("SMTP_HOST"),
    Port = int.Parse(Environment.GetEnvironmentVariable("SMTP_PORT")),
    UserName = Environment.GetEnvironmentVariable("SMTP_USER"),
    Password = Environment.GetEnvironmentVariable("SMTP_PASSWORD"),
    Name = "Sender display name",
});

3. Send. That is the whole setup.

✈  · · ·  ✉

Compatibility

Target framework Supported
.NET 10.0 ✅
.NET 9.0 ✅
.NET 8.0 ✅
.NET Standard 2.1 ✅
.NET Standard 2.0 ✅

.NET Standard 2.0 covers .NET Framework 4.6.1+, .NET Core 2.0+ and Mono/Xamarin/Unity.

Dependencies: MailKit, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Configuration.Binder.

Upgrading from 1.0.x? Nothing to change — see the migration guide.

Documentation

Getting Started Install, configure, first mail — sync and async
Configuration Every SmtpMailConfig field, environment variables, common mail providers
API Reference Every public member, parameter and exception
Migration 1.0.x → 1.1.0
Changelog Version history

License

MIT.

Support

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 130 8/27/2026
1.1.0 108 8/27/2026
1.0.2 293 12/24/2025
1.0.1 649 3/22/2022
1.0.0 590 3/22/2022

v1.2.0 — the sender address and the login account can now be separate. No breaking changes: leaving SenderAddress empty behaves exactly like 1.1.0.

ADDED:
- VSmtpMailConfig.SenderAddress (optional, defaults to an empty string). When set, it becomes the sender address and UserName is used for SMTP authentication only — for providers whose SMTP credentials are not a mailbox address, such as AWS SES. When empty, UserName is still used as the sender address, exactly as in 1.1.0.
- Unauthenticated relays can now carry a valid sender. Leave UserName empty to skip authentication (as in 1.1.0) and set SenderAddress to the address to send from; on 1.1.0 that combination produced a message with no sender address.

TECHNICAL:
- xUnit tests covering the sender selection logic: SenderAddress set, SenderAddress empty falling back to UserName, and UserName empty combined with SenderAddress.

Full changelog: https://github.com/ozakboy/ozakboy.Mail/blob/main/docs/en/changelog.md