コンテンツプロバイダーによる抽象化された電子メール送信者

Aug 17 2020

メールコンテンツのプロバイダーを含むメール送信者を作成しました。これは、メールの種類に基づいて変更されます。私はそれを強化するためにいくつかの助けが必要です。

これらの2つのモデルは送信に使用されます

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; }
}

コンテンツプロバイダーは、すべての電子メール情報(件名、本文、宛先、およびCc)を提供します。

public interface IEmailContentProvider
{
    EmailMessage Message { get; }
}

次に、パラメータを使用して電子メール情報を取得IEmailSenderする単一のメソッドSendを持つ抽象化された電子メール送信者がありIEmailContentProviderます

interface IEmailSender
    {
        Task Send(IEmailContentProvider provider);
    }

コンテンツプロバイダーの例があります 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} }
        };
    }
}

IEmailSender実装:

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);
    }
}

そして、これがそれを使用して電子メールを送信する方法です:

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

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

回答

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

必須ではありませんが、開始しただけで理解できますToAddressが、このようなリストを開始すると、大量のメモリを消費する可能性があります(EmailMessageインスタンスが大量にあると想像してください!したがって、それらをnullのままにして、null検証を使用して強制することをお勧めします(送信者のように)必要なときにそれらを開始します。

全体的なデザインは十分ですが、これを直接行うことができます。

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; }
}

EmailMessageメッセージの完全なモデルとして、From(必須)、To(必須)、CC(オプション)、BCC(オプション)、件名、および本文を含める必要があります。その背後にある理由は、メッセージの要件として常にペアになり、後でデータベース側で実装するのも簡単になるためです。usingIEnumerable<EmailAddress>は、を実装する任意のコレクションを使用するために開きますIEnumerable。したがって、に限定されませんList

以下のためのIEmailProvider電子メールプロバイダも要件として独自の設定を含める必要があります。これにより、各電子メールメッセージに独自の設定を設定することが容易になります。このようにして、追加のコーディングなしで、異なるプロバイダーから複数の電子メールを送信できます。IOptionsは独立して扱われますが、実際にIOptionsはそれ自体では役に立たず、によってのみ使用されるEmailProviderため、コントラクトに追加すると、常にインターフェイスで実装されます。このようにして、プロバイダーに常に持っていIOptionsます。また、IOptionsのようなものに名前を変更しなければならないEmailServerSettingか、EmailProviderSettingメインの実装にそれを関連付けるために。これらは設定であり、オプションではありません。IOptionsテキストやHTMLとして送信したり、添付ファイルや写真などを無効/有効にしたりするなど、管理できるメールのオプション設定や機能を処理するために使用します。

また、物事をまとめるためのミドルウェアクラスが必要であり、を使用しSmtpClientます。これにより、現在の作業の柔軟性が高まり、新しい送信者ごとにコードを再実装するのではなくMimeMessage、コードを再利用するための作業が簡単になるとともに、それらを1つの屋根の下にラップするという利点が得られますTextPart。また、これまでのところ、複数のプロバイダーを格納するためのコレクションを作成することもできます。また、新しいプロバイダーを追加し、それらを処理し、メッセージを処理し、作業の範囲を維持することができます。

これが私が最終的な使用法を想像する方法です:

新しいプロバイダーの作成:

// 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 
    }

今、新しいメールメッセージを作成して送信します:

// 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();  
}