ผู้ส่งอีเมลที่เป็นนามธรรมกับผู้ให้บริการเนื้อหา

Aug 17 2020

ฉันได้สร้างผู้ส่งอีเมลกับผู้ให้บริการสำหรับเนื้อหาอีเมลซึ่งจะมีการเปลี่ยนแปลงตามประเภทอีเมล ฉันต้องการความช่วยเหลือเพื่อปรับปรุง

สองรุ่นนี้ใช้ส่ง

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

ผู้ให้บริการเนื้อหาให้ข้อมูลอีเมลทั้งหมด (หัวเรื่องเนื้อความถึงและสำเนา)

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การตรวจสอบความถูกต้องเพื่อบังคับ เริ่มต้นเมื่อจำเป็น (เช่นในผู้ส่ง)

การออกแบบโดยรวมของคุณดีพออย่างไรก็ตามคุณสามารถทำได้โดยตรง:

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 (required), To (required), CC (optional), BCC (optional), Subject, and Body เป็นรูปแบบข้อความทั้งหมด เหตุผลเบื้องหลังก็คือมันจะถูกจับคู่กันเสมอตามความต้องการของข้อความใด ๆ ในภายหลังมันจะง่ายกว่าที่จะนำไปใช้ในด้านฐานข้อมูล ใช้IEnumerable<EmailAddress>จะเปิดใช้งานคอลเลกชันใด ๆ IEnumerableที่นำไปปฏิบัติ ดังนั้นจึงไม่ได้ Listจำกัด

สำหรับIEmailProviderในฐานะผู้ให้บริการอีเมลควรมีการตั้งค่าของตนเองตามความต้องการ สิ่งนี้จะทำให้คุณง่ายขึ้นในการมีข้อความอีเมลแต่ละฉบับพร้อมการตั้งค่าของตัวเอง ด้วยวิธีนี้คุณสามารถส่งอีเมลหลายฉบับจากผู้ให้บริการที่แตกต่างกันโดยไม่ต้องเข้ารหัสเพิ่มเติม IOptionsได้รับการปฏิบัติโดยอิสระ แต่ในความเป็นจริงIOptionsจะไม่เป็นประโยชน์ด้วยตัวมันเองและจะใช้โดยEmailProviderดังนั้นหากเราเพิ่มลงในสัญญามันจะถูกนำไปใช้กับอินเทอร์เฟซเสมอวิธีนี้คุณบังคับให้ผู้ให้บริการเสมอ มีIOptions. นอกจากนี้IOptionsควรเปลี่ยนชื่อเป็นสิ่งที่เหมือนEmailServerSettingหรือEmailProviderSettingเพื่อให้เกี่ยวข้องกับการใช้งานหลัก เนื่องจากเป็นการตั้งค่าไม่ใช่ตัวเลือก ใช้IOptionsจัดการการตั้งค่าอีเมลเสริมและคุณสมบัติที่สามารถจัดการได้เช่นส่งเป็นข้อความหรือ html ปิด / เปิดใช้งานไฟล์แนบรูปภาพ .. ฯลฯ

SmtpClientนอกจากนี้คุณต้องชนชั้นกลางเครื่องห่อสิ่งขึ้นและการใช้งาน นี้จะให้คุณประโยชน์ของการเพิ่มความยืดหยุ่นในการทำงานในปัจจุบันของคุณและห่อไว้ภายใต้หนึ่งหลังคาพร้อมกับสิ่งขึ้นสำหรับโค้ดความสะดวก (เช่นMimeMessage, TextPart..etc.) แทน reimplement มันในแต่ละผู้ส่งใหม่ นอกจากนี้ยังช่วยให้คุณสามารถสร้างคอลเลกชันเพื่อจัดเก็บผู้ให้บริการหลายรายหากคุณไปไกล นอกจากนี้คุณจะสามารถเพิ่มผู้ให้บริการรายใหม่จัดการพวกเขาจัดการข้อความและกำหนดขอบเขตงานของคุณได้

นี่คือวิธีที่ฉันจินตนาการถึงการใช้งานขั้นสุดท้าย:

การสร้างผู้ให้บริการใหม่:

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