Regira Office.Mail provides a unified abstraction for sending email through multiple providers. All implementations share the same IMailService interface, making mail backends interchangeable in consuming code.
| Project | Package | Backend |
|---|---|---|
Common.Office |
(transitive) | Shared abstractions, models, and DummyMailer |
Mail.SendGrid |
Regira.Office.Mail.SendGrid |
SendGrid API |
Mail.MailGun |
Regira.Office.Mail.MailGun |
Mailgun REST API |
Mail.Web |
Regira.Office.Mail.Web |
HTTP request DTOs for mail endpoints |
Mail.MSGReader |
Regira.Office.Mail.MSGReader |
Read existing .msg and .eml files |
Security.Authentication.Web |
Regira.Security.Authentication.Web |
IdentityMailer bridge to ASP.NET Identity’s IEmailSender |
<!-- SendGrid -->
<PackageReference Include="Regira.Office.Mail.SendGrid" Version="6.*" />
<!-- Mailgun -->
<PackageReference Include="Regira.Office.Mail.MailGun" Version="6.*" />
<!-- Mail.Web -->
<PackageReference Include="Regira.Office.Mail.Web" Version="6.*" />
<!-- Mail.MSGReader -->
<PackageReference Include="Regira.Office.Mail.MSGReader" Version="6.*" />
<!-- IdentityMailer (ASP.NET Identity integration) -->
<PackageReference Include="Regira.Security.Authentication.Web" Version="6.*" />
IServiceCollection services = new ServiceCollection();
IConfiguration configuration = new ConfigurationBuilder().Build();
// Register (pick one)
services.AddSendGrid(cfg => cfg.Key = configuration["Mail:SendGrid:Key"]!);
// or
services.AddMailGun(cfg =>
{
cfg.Api = configuration["Mail:MailGun:Api"]!;
cfg.Key = configuration["Mail:MailGun:Key"]!;
cfg.Domain = configuration["Mail:MailGun:Domain"]!;
});
// Use — the parameters are interface-typed, so construct the concrete models
// (the implicit string conversions don't apply to IMailAddress/IMailRecipient)
IMailService mailer = services.BuildServiceProvider().GetRequiredService<IMailService>();
await mailer.Send(
sender: new MailAddress { Email = "no-reply@example.com" },
recipients: [new MailRecipient { Email = "alice@example.com" }],
subject: "Hello",
message: "<p>Hi!</p>"
);
Both backends implement this interface.
```csharp no-compile
// Parameter-based overload
Task
// Full message overload
Task
### IMailResponse
| Property | Type | Description |
|----------|------|-------------|
| `Success` | `bool` | `true` when the provider accepted the message |
| `Status` | `string?` | HTTP status code or provider status text |
| `Content` | `string?` | Raw response body |
| `Exception` | `Exception?` | Set when sending fails |
## Core Models
### IMessageObject / MessageObject
Represents a complete outgoing email.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `From` | `MailAddress?` | `null` | Sender address |
| `To` | `ICollection<MailRecipient>` | `[]` | Recipients (To / Cc / Bcc) |
| `ReplyTo` | `IMailAddress?` | `null` | Reply-To address |
| `Subject` | `string?` | `null` | Email subject |
| `Body` | `string?` | `null` | Message body |
| `IsHtml` | `bool` | `true` | HTML vs plain text |
| `Attachments` | `ICollection<BinaryFileItem>?` | `null` | File attachments |
`MessageObject` exposes the concrete model types shown above; the interface-typed members of
`IMessageObject` (`IMailAddress? From`, `ICollection<IMailRecipient> To`, `ICollection<INamedFile>? Attachments`)
are implemented explicitly and convert to/from the concrete types.
### IMailAddress / MailAddress
| Property | Type | Description |
|----------|------|-------------|
| `Email` | `string` | Email address — validated on assignment |
| `DisplayName` | `string?` | Optional display name |
`MailAddress` supports implicit conversion from `string`:
```csharp
MailAddress addr = "alice@example.com";
MailAddress named = new() { Email = "alice@example.com", DisplayName = "Alice" };
ToString() returns "Alice <alice@example.com>" when DisplayName is set, or just the email.
Extends IMailAddress with a recipient type.
public enum RecipientTypes { To, Cc, Bcc }
MailRecipient to = "alice@example.com"; // implicit — defaults to RecipientTypes.To
var cc = new MailRecipient { Email = "bob@example.com", RecipientType = RecipientTypes.Cc };
var bcc = new MailRecipient { Email = "carol@example.com", RecipientType = RecipientTypes.Bcc };
| Property | Type | Description |
|---|---|---|
Key |
string |
SendGrid API key |
| Property | Type | Description |
|---|---|---|
Api |
string |
Mailgun API endpoint (e.g. https://api.mailgun.net/v3) |
Key |
string |
Mailgun API key |
Domain |
string |
Sending domain |
TestMode |
bool |
Sends with Mailgun’s o:testmode flag. Default false. |
With TestMode enabled, Mailgun validates, accepts and logs each call exactly as it would a real one, but
never delivers it to the recipient. The response is a normal success, so code and tests that check
response.Success are unaffected. Note that it suppresses delivery, not billing — message counts and
charges may still apply.
IServiceCollection services = new ServiceCollection();
// SendGrid
services.AddSendGrid(cfg => cfg.Key = "SG.xxx");
// Mailgun
services.AddMailGun(cfg =>
{
cfg.Api = "https://api.mailgun.net/v3";
cfg.Key = "key-xxx";
cfg.Domain = "mail.example.com";
});
// Mailgun, accepted and logged but never delivered — for staging hosts and test suites
// that send to real addresses
services.AddMailGun(cfg =>
{
cfg.Api = "https://api.mailgun.net/v3";
cfg.Key = "key-xxx";
cfg.Domain = "mail.example.com";
cfg.TestMode = true;
});
Both extension methods register IMailService as a transient service.
Thrown by the shared MailerBase for invalid attachments (missing file name or empty content), and by
SendGrid when the provider returns a non-success response. The Mailgun backend throws a plain
Exception on failure instead — a catch (MailException) block will not catch Mailgun send failures.
| Property | Type | Description |
|---|---|---|
MessageObject |
IMessageObject? |
The message that failed to send |
ResponseContent |
string? |
Raw provider response body |
Thrown when an invalid email address is assigned to MailAddress.Email.
| Property | Type | Description |
|---|---|---|
EmailInput |
string? |
The invalid value that was provided |
DummyMailer implements IMailService (via MailerBase) and sends nothing — it returns an empty MailResponse, so Success is false. Register it in tests to suppress actual sending:
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IMailService, DummyMailer>();
Mail.Web ships MailInput for accepting email requests over HTTP. MailInputExtensions.ToMessageObject() converts it to a domain IMessageObject.
```csharp no-compile
[HttpPost]
public async Task
### MailInput structure
| Property | Type | Validation | Description |
|----------|------|------------|-------------|
| `From` | `Address?` | — | Optional sender override |
| `To` | `ICollection<Recipient>?` | `[Required]` | Recipients |
| `ReplyTo` | `Address?` | — | Reply-To address |
| `Subject` | `string?` | `[Required]` | Email subject |
| `Body` | `string?` | — | HTML or plain text body |
| `IsHtml` | `bool` | — | Defaults to `true` |
| `Attachments` | `ICollection<Attachment>?` | — | File attachments |
`Address` and `Recipient` both support implicit conversion from a plain email string.
## ASP.NET Identity Integration
`IdentityMailer` (from the `Regira.Security.Authentication.Web` package, namespace
`Regira.Security.Authentication.Web.Mail`) bridges `IMailService` to the ASP.NET Identity `IEmailSender` interface.
```csharp
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IEmailSender>(provider =>
new IdentityMailer(
provider.GetRequiredService<IMailService>(),
new IdentityMailerOptions { Sender = "no-reply@example.com" }
));