Foreword
In actual project development, we often encounter scenarios where emails need to be sent programmatically, such as anomaly alerts, notifications, progress updates, etc. In most cases, we rely on the native SmtpClient class library, which can satisfy most of our needs. However, it is not concise enough to use, and many scenarios require us to manually wrap methods, leading to a significant amount of code. Fortunately, there is an excellent component that meets most of our application scenarios, is simple to use, and very powerful – that is, FluentEmail, which we are going to discuss today. This is the email sending component we are actually using in our projects. If you have a need to send emails in .NET Core, I recommend giving it a try.
FluentEmail
FluentEmail is an open-source, free email sending component available on GitHub that supports .NET and .NET Core. It currently has over 1K Stars, and with the maturation of .NET Core in recent years, its Star growth trend has been quite strong. Its GitHub address is https://github.com/lukencode/FluentEmail. It is very powerful and practical, supporting Razor email templates and sending emails via SendGrid, MailGun, SMTP, and is very simple to use.
NuGet Components
FluentEmail is powerful and provides separate NuGet packages for different scenarios. This low-coupling separation not only makes dependencies very clear but also avoids introducing unnecessary code. The specific functions are included in the following component packages:
- FluentEmail.Core - The base core package, containing basic model definitions and default settings. All the following reference packages include this core package.
- FluentEmail.Smtp - A package for sending emails using SMTP service.
- FluentEmail.Razor - Generates email content via Razor templates.
- FluentEmail.Mailgun - Sends emails using the Mailgun REST API.
- FluentEmail.SendGrid - Sends emails using the SendGrid API.
- FluentEmail.Mailtrap - Sends emails via Mailtrap, using the FluentEmail.Smtp package for sending.
- FluentEmail.MailKit - Sends emails using the MailKit email library.
Sending Plain Emails
Next, we will demonstrate how to send emails using FluentEmail. Since most of our actual business uses SMTP to send emails, we will demonstrate with that. First, we need to introduce the FluentEmail.Smtp package into our project. The current latest version is 2.8.0.
<PackageReference Include="FluentEmail.Smtp" Version="2.8.0" />
Now we can happily write code. The coding approach is very simple and concise, mainly using fluent programming.
// If using SMTP service to send emails, you must set SMTP service information
SmtpClient smtp = new SmtpClient
{
// SMTP server address (Here I use 126 mail as an example; set according to the email provider you are using)
Host = "smtp.126.com",
UseDefaultCredentials = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
// Enter the username and password for the SMTP server you are using
Credentials = new NetworkCredential("email username", "email password")
};
// Set default sender information
Email.DefaultSender = new SmtpSender(smtp);
var email = Email
// From
.From("zhangsan@126.com")
// To
.To("lisi@qq.com")
// CC
.CC("admin@126.com")
// Subject
.Subject("Email Subject")
// Body
.Body("Email Body");
// Check if sending was successful based on the result
var result = email.Send();
// Or use async sending
// await email.SendAsync();
if (result.Successful)
{
// Logic for successful sending
}
else
{
// If failed, you can check the failure reasons via result.ErrorMessages
}
If the content you are sending contains HTML, you can use the following approach:
var email = Email
// From
.From("zhangsan@126.com")
// To
.To("lisi@qq.com")
// CC
.CC("admin@126.com")
// Subject
.Subject("Email Subject")
// Simply set the second parameter to true
.Body("<h1 align=\"center\">.NET is Great</h1><p>Yes, absolutely no problem</p>", true);
// Send
var result = email.Send();
By checking the method declaration of Body, we can see that the second parameter indicates whether the content is HTML (default is false).
IFluentEmail Body (string body, bool isHtml = false);
If the email has multiple recipients, you can use another overload of the To method that accepts List<FluentEmail.Core.Models.Address>.
var email = Email
// From
.From("zhangsan@126.com")
// Subject
.Subject("Email Subject")
// Body
.Body("<h1 align=\"center\">.NET is Great</h1><p>Yes, absolutely no problem</p>", true);
// Build multiple recipient emails
string toUserStr = "oldwang@126.com;xiaoming@163.com;xiaoli@qq.com";
List<FluentEmail.Core.Models.Address> toUsers = toUserStr.Split(";")
.Select(i => new FluentEmail.Core.Models.Address { EmailAddress = i }).ToList();
// Supports passing an Address collection
email.To(toUsers)
// CC collection
.CC(toUsers);
// Send
var result = email.Send();
If we need to add an attachment to the email, we can use the Attach method.
var email = Email
// From
.From("zhangsan@qq.com")
// To
.To("lisi@126.com")
// CC
.CC("admin@126.com")
// Subject
.Subject("About .NET Core")
// Body
.Body("<h1 align=\"center\">.NET Core</h1><p>Is .NET Core excellent? Yes, absolutely no problem!!!</p>", true);
// Build attachment
var stream = new MemoryStream();
var sw = new StreamWriter(stream);
sw.WriteLine("Hello, this is the content of the text file.");
sw.Flush();
stream.Seek(0, SeekOrigin.Begin);
var attachment = new FluentEmail.Core.Models.Attachment
{
Data = stream,
ContentType = "text/plain",
Filename = "Hello.txt"
};
// Add attachment
email.Attach(attachment);
var result = email.Send();
If you need to add multiple attachments, the Attach method supports passing an Attachment collection.
// Build attachments
var stream = new MemoryStream();
var sw = new StreamWriter(stream);
sw.WriteLine("Hello, this is the content of the text file.");
sw.Flush();
stream.Seek(0, SeekOrigin.Begin);
// Attachment 1
var attachment = new FluentEmail.Core.Models.Attachment
{
Data = stream,
ContentType = "text/plain",
Filename = "Hello.txt"
};
// Attachment 2
var attachment2 = new FluentEmail.Core.Models.Attachment
{
Data = File.OpenRead(@"D:\test.txt"),
ContentType = "text/plain",
Filename = "test.txt"
};
// Add attachments
email.Attach(new List<FluentEmail.Core.Models.Attachment> { attachment, attachment2 });
var result = email.Send();
Using Razor Templates
The above content introduced sending emails using FluentEmail in the conventional way. However, sometimes we need to send dynamic content or more complex HTML page content. When using the native SmtpClient, we usually concatenate HTML code, which is relatively time-consuming and labor-intensive. For .NET developers, the Razor engine is the most familiar way to build dynamic HTML pages. FluentEmail provides support for Razor templates. First, we need to introduce the FluentEmail.Razor template support component on top of what we already have.
<PackageReference Include="FluentEmail.Razor" Version="2.8.0" />
Since ASP.NET Core 2.2, the default view compilation feature is enabled, and views are compiled into ProjectName.Views.dll. However, FluentEmail.Razor needs to read the content of the view files, so we need to add the following to the .csproj file:
<MvcRazorExcludeRefAssembliesFromPublish>true</MvcRazorExcludeRefAssembliesFromPublish>
Then we can use Razor templates to generate email content. The specific usage is:
// Declare the use of Razor
Email.DefaultRenderer = new RazorRenderer();
// Razor content
var template = "Hello @Model.Name, please verify your phone number is @Model.Phone吗";
var email = Email
.From("lisi@126.com")
.To("zhangsan@qq.com")
.Subject("Phone Number Verification")
// Pass a custom POCO class
//.UsingTemplate<UserInfo>(template, new UserInfo { Name = "Zhang San", Phone吗 = "100110119120" })
// Or pass an anonymous object
.UsingTemplate(template, new { Name = "Zhang San", Phone吗 = "100110119120" });
var result = await email.SendAsync();
Of course, it supports more than just Razor strings; you can also pass Razor view files.
var email = Email
.From("lisi@126.com")
.To("zhangsan@qq.com")
.Subject("Phone Number Verification")
// Pass a custom POCO class
//.UsingTemplateFromFile<UserInfo>($"{Directory.GetCurrentDirectory()}/template.cshtml",
// new UserInfo { Name = "Zhang San", Phone吗 = "100110119120" });
// The first parameter is the view file path, the second is the model object
.UsingTemplateFromFile($"{Directory.GetCurrentDirectory()}/template.cshtml",
new { Name = "Zhang San", Phone吗 = "100110119120" });
var result = await email.SendAsync();
FluentEmail.Razor can support such a powerful Razor template engine mainly because it integrates RazorLight internally. RazorLight is a very powerful Razor engine that can parse Razor template strings or Razor view files into specific string results. For details, please refer to the official RazorLight GitHub address https://github.com/toddams/RazorLight. The official stable version does not support .NET Core, but you can choose to download the beta version:
Install-Package RazorLight -Version 2.0.0-beta10
Its usage is also very simple:
// Razor string approach
var engine = new RazorLightEngineBuilder()
.UseEmbeddedResourcesProject(typeof(Program))
.UseMemoryCachingProvider()
.Build();
string template = "Hello, @Model.Name. Welcome to RazorLight repository";
ViewModel model = new ViewModel {Name = "John Doe"};
// result is the parsed string
string result = await engine.CompileRenderStringAsync("templateKey", template, model);
Or using the Razor view file approach:
var engine = new RazorLightEngineBuilder()
.UseFileSystemProject("${Directory.GetCurrentDirectory()}")
.UseMemoryCachingProvider()
.Build();
var model = new {Name = "John Doe"};
string result = await engine.CompileRenderAsync("template.cshtml", model);
Of course, the supported approaches are not limited to these two. Both in terms of convenience and functionality, it is very powerful. Interested students can check out the RazorLight GitHub address for more detailed explanations. We will not discuss the usage of RazorLight too much here.
Regarding the email content sent, there is a very important point to kindly remind: Public email providers like NetEase or Tencent may require manually enabling SMTP service. For specific settings, please refer to this article. Another important point: If you use a public email provider, they may impose significant restrictions on email subjects and content, leading to various issues. Moreover, enabling SMTP service may require SMS authentication to activate. Fortunately, most companies have their own email systems, so there may not be so many issues in actual email sending.
Use with Dependency Injection
In actual development with .NET Core, dependency injection has become an indispensable development pattern. If you are using .NET Core to develop projects but are not yet familiar with dependency injection, you need to reflect. FluentEmail, as a component that keeps up with the times, can also be used with dependency injection. This way, we can configure some default settings uniformly during registration. This operation does not require additional packages. If you need to use SMTP, introduce the FluentEmail.Smtp package; if you need Razor templates, introduce the FluentEmail.Razor package. The injection part is actually included in the FluentEmail.Core package.
public void ConfigureServices(IServiceCollection services)
{
SmtpClient smtp = new SmtpClient
{
// SMTP server address (Here I use QQ mail as an example; set according to the email provider)
Host = "smtp.qq.com",
UseDefaultCredentials = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
// Enter the username and password for the SMTP server
Credentials = new NetworkCredential("zhangsan@qq.com", "zhangsan")
};
// Add some default settings during injection
services
// Set default sender
.AddFluentEmail("zhangsan@qq.com")
// Add Razor template support
//.AddRazorRenderer($"{Directory.GetCurrentDirectory()}/Views")
.AddRazorRenderer()
// Configure default SMTP service information
.AddSmtpSender(smtp);
}
In the class where you need to send emails, inject IFluentEmail directly. Don't panic – the Email class we used earlier actually implements the IFluentEmail interface, so the usage is exactly the same.
public async Task<IActionResult> SendEmail([FromServices]IFluentEmail email)
{
var result = await email
// From
.From("zhangsan@126.com")
// To
.To("lisi@qq.com")
// CC
.CC("admin@126.com")
// Subject
.Subject("Email Subject")
// Body
.Body("Email Body").SendAsync();
return View();
}
If you need to send Razor view template content, it's still the familiar recipe and familiar taste – no difference at all, just omitting some default configurations we added during registration.
public async Task<IActionResult> SendEmail([FromServices]IFluentEmail email)
{
var template = "Hello @Model.Name, please verify your phone number is @Model.Phone吗";
var result = await email
.From("lisi@126.com")
.To("zhangsan@qq.com")
.Subject("Phone Number Verification")
// Pass a custom POCO class
//.UsingTemplate<UserInfo>(template, new UserInfo { Name = "Zhang San", Phone吗 = "100110119120" })
// Or pass an anonymous object
.UsingTemplate(template, new { Name = "Zhang San", Phone吗 = "100110119120" })
.SendAsync();
return View();
}
Summary
We have introduced the basic usage of FluentEmail. Personally, I think its functions are very powerful and its usage is very simple. To be honest, before I got to know FluentEmail, I often saw email sending components integrated in other languages in the community, which are indeed very powerful, such as spring-boot-starter-mail in Spring Boot, which is very convenient. Later, I accidentally came across FluentEmail, and I felt quite pleased – firstly because of its powerful features and ease of use, and secondly because it can be combined with .NET Core to further optimize its usage. At least in .NET and .NET Core, we also have a very convenient email sending component. The author of FluentEmail also encourages more developers to understand and participate in the development and practice of FluentEmail. Finally, I attach its GitHub address: https://github.com/lukencode/FluentEmail. If you are interested, you can check it out and don't forget to give a Star.