Pengirim email abstrak dengan penyedia konten

Aug 17 2020

Saya telah membuat pengirim email dengan penyedia konten email, yang akan diubah berdasarkan jenis email. Saya butuh bantuan untuk meningkatkannya.

Kedua model ini digunakan untuk mengirim

public class EmailAddress
{
    public string Name { get; set; }
    public string Address { get; set; }
}

public class EmailMessage
{
    public EmailMessage()
    {
        ToAddresses = new List<EmailAddress>();
        CcAddresses = new List<EmailAddress>();
    }

    public List<EmailAddress> ToAddresses { get; set; }
    public List<EmailAddress> CcAddresses { get; set; }
    public string Subject { get; set; }
    public string Content { get; set; }
}

Penyedia konten menyediakan semua informasi email (subjek, isi, Kepada dan Cc)

public interface IEmailContentProvider
{
    EmailMessage Message { get; }
}

Kemudian kami memiliki pengirim email abstrak IEmailSenderyang memiliki metode tunggal Sendyang menggunakan IEmailContentProviderparameter untuk mendapatkan informasi email

interface IEmailSender
    {
        Task Send(IEmailContentProvider provider);
    }

Saya punya contoh untuk penyedia konten WelcomEmailProvider

public class WelcomEmailProvider : IEmailProvider
{
        public EmailMessage Message { get; }

        public WelcomEmailProvider(string address, string name)
        {
            Message = new EmailMessage
        {
            Subject = $"Welcome {name}", Content = $"This is welcome email provider!",
            ToAddresses = new List<EmailAddress> { new EmailAddress { Address = address, Name = name} }
        };
    }
}

The IEmailSenderimplementasi:

public class EmailSender : IEmailSender
{
    private readonly SmtpOptions _options;

    public EmailSender(IOptions<SmtpOptions> options)
    {
        _options = options.Value;
    }

    public async Task Send(IEmailContentProvider provider)
    {
        var emailMessage = provider.Message;
        var message = new MimeMessage();
        message.From.Add(new MailboxAddress(_options.Sender.Name, _options.Sender.Address));
        message.To.AddRange(emailMessage.ToAddresses.Select(x => new MailboxAddress(x.Name, x.Address)));
        message.Cc.AddRange(emailMessage.CcAddresses.Select(x => new MailboxAddress(x.Name, x.Address)));

        message.Subject = emailMessage.Subject;
        message.Body = new TextPart(TextFormat.Html) { Text = emailMessage.Content };

        using var emailClient = new SmtpClient();
        await emailClient.ConnectAsync(_options.Server, _options.Port, _options.EnableSsl);
        await AuthenticatedData(emailClient);

        await emailClient.SendAsync(message);
        await emailClient.DisconnectAsync(true);

    }

    private async Task AuthenticatedData(SmtpClient smtpClient)
    {
        if (string.IsNullOrWhiteSpace(_options.Username) || string.IsNullOrWhiteSpace(_options.Password))
            return;

        emailClient.AuthenticationMechanisms.Remove("XOAUTH2");
        await emailClient.AuthenticateAsync(_options.Username, _options.Password);
    }
}

Dan inilah, cara menggunakannya dan mengirim email:

class Sample
{
    private readonly IEmailSender _emailSender;
    public Samole(IEmailSender emailSender)
    {
        _emailSender = emailSender;
    }

    public async Task DoSomethingThenSendEmail()
    {
        await _emailSender.Send(new WelcomEmailProvider("[email protected]", "Someone"));
    }
}

Jawaban

1 iSR5 Sep 01 2020 at 03:40
public EmailMessage()
{
    ToAddresses = new List<EmailAddress>();
    CcAddresses = new List<EmailAddress>();
}

Tidak perlu, saya dapat mengerti jika Anda baru saja memulai ToAddress, namun, memulai daftar seperti ini mungkin menghabiskan banyak memori (bayangkan Anda memiliki jumlah EmailMessagecontoh yang besar! Jadi, saya sarankan untuk menyimpannya sebagai null, dan gunakan nullvalidasi untuk memaksa memulai mereka saat dibutuhkan (seperti di pengirim).

keseluruhan desain Anda sudah cukup bagus, namun Anda dapat melakukannya secara langsung:

public class EmailMessage 
{
    public EmailAddress From { get; set; }
    public IEnumerable<EmailAddress> To { get; set; }
    public IEnumerable<EmailAddress> Cc { get; set; }
    public IEnumerable<EmailAddress> Bcc { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}

public interface IEmailProvider
{
    IEmailServerSetting ServerSettings { get; set; }
}

EmailMessageharus berisi Dari (wajib), Kepada (wajib), CC (opsional), BCC (opsional), Subjek, dan Isi sebagai model lengkap pesan. Alasan di balik itu adalah bahwa itu akan selalu dipasangkan bersama sebagai persyaratan untuk pesan apa pun, nantinya juga akan lebih mudah diimplementasikan di sisi database. menggunakan IEnumerable<EmailAddress>akan terbuka untuk menggunakan koleksi apa pun yang mengimplementasikan IEnumerable. Jadi, itu tidak terbatas pada List.

Karena IEmailProviderSebagai penyedia email juga harus memuat pengaturannya sendiri sebagai persyaratan. Ini akan mempermudah Anda, untuk memiliki setiap pesan email dengan pengaturannya sendiri. Dengan cara ini, Anda dapat mengirim banyak email dari penyedia yang berbeda tanpa pengkodean tambahan. IOptionsdiperlakukan secara independen, tetapi pada kenyataannya, IOptionstidak akan berguna dengan sendirinya, dan hanya akan digunakan oleh EmailProvider, jadi jika kami menambahkannya ke kontrak, itu akan selalu diimplementasikan dengan antarmuka, dengan cara ini Anda memaksa penyedia untuk selalu punya IOptions. Selain itu, IOptionsharus diubah namanya menjadi sesuatu seperti EmailServerSettingatau EmailProviderSettingmengaitkannya dengan implementasi utama. Karena ini adalah pengaturan dan bukan opsi. MenggunakanIOptions untuk menangani pengaturan opsional email dan fitur yang dapat dikelola seperti mengirim sebagai teks atau html, nonaktifkan / aktifkan lampiran, gambar .. dll.

Juga, Anda membutuhkan kelas menengah untuk menyelesaikan semuanya, dan menggunakan SmtpClient. Ini akan memberi Anda keuntungan dalam meningkatkan fleksibilitas pekerjaan Anda saat ini dan menggabungkannya di bawah satu atap bersama dengan kemudahan untuk menggunakan kembali kode (seperti MimeMessage, TextPart..etc.) Daripada menerapkannya kembali pada setiap pengirim baru. Ini juga akan memberi Anda kemampuan untuk membuat koleksi untuk menyimpan banyak penyedia jika Anda melangkah sejauh itu. juga, Anda akan dapat menambahkan penyedia baru, menanganinya, menangani pesan, dan menjaga cakupan pekerjaan Anda.

Berikut adalah bagaimana saya membayangkan penggunaan akhir:

Membuat penyedia baru:

// creating a new email provider 
    public class SomeEmailProvider : IEmailProvider
    {
        // only set the settings internally but it's exposed to be readonly
        public EmailServerSetting ServerSettings { get; private set; }
        
        public SomeEmailProvider()
        {
            //set up the server settings
            ServerSettings = new EmailServerSetting 
            {
                ServerType = EmailServerType.POP, 
                Server = "pop.mail.com",
                Port = 995, 
                Encryption = EmailServerEncryption.SSLOrTLS, 
                EnableSsl = true
            };      
        }
       // some other related code 
    }

sekarang, membuat pesan email baru dan mengirimkannya:

// can be single object or collection 
var messages = new EmailMessage
{
    From = new EmailAddress("Test", "[email protected]"), 
    To = new EmailAddress [] {
         new EmailAddress("Test1", "[email protected]"), 
         new EmailAddress("Test2", "[email protected]")
        }, 
    Subject = "Testing Subject", 
    Body = "Normal Text Body"           
};

using(var client = new EmailClient(new SomeEmailProvider()))
{
    client.Options = new EmailClientOptions 
    {
        // would send it as plain text 
        EnableHtml = false 
    };
    
    client.Messages.Add(messages); 
    
    client.Send();  
}