Abstrahierter E-Mail-Absender mit Inhaltsanbietern
Ich habe einen E-Mail-Absender mit Anbietern für den E-Mail-Inhalt erstellt, der je nach E-Mail-Typ geändert wird. Ich brauche etwas Hilfe, um es zu verbessern.
Diese beiden Modelle werden zum Senden verwendet
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; }
}
Der Inhaltsanbieter stellt alle E-Mail-Informationen bereit (Betreff, Text, An und Cc).
public interface IEmailContentProvider
{
EmailMessage Message { get; }
}
Dann haben wir den abstrahierten E-Mail-Absender IEmailSender, der eine einzige Methode hat Send, die IEmailContentProviderParameter verwendet, um die E-Mail-Informationen abzurufen
interface IEmailSender
{
Task Send(IEmailContentProvider provider);
}
Ich habe ein Beispiel für den InhaltsanbieterWelcomEmailProvider
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} }
};
}
}
Die IEmailSenderUmsetzung:
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);
}
}
Und hier ist, wie man es benutzt und eine E-Mail sendet:
class Sample
{
private readonly IEmailSender _emailSender;
public Samole(IEmailSender emailSender)
{
_emailSender = emailSender;
}
public async Task DoSomethingThenSendEmail()
{
await _emailSender.Send(new WelcomEmailProvider("[email protected]", "Someone"));
}
}
Antworten
public EmailMessage()
{
ToAddresses = new List<EmailAddress>();
CcAddresses = new List<EmailAddress>();
}
Nicht notwendig, ich kann verstehen, wenn Sie nur die initiieren ToAddress, aber das Initiieren der Listen auf diese Weise kann viel Speicher verbrauchen (stellen Sie sich vor, Sie haben eine große Anzahl von EmailMessageInstanzen! Ich würde also vorschlagen, sie als null zu belassen und die nullValidierung zu erzwingen Initiieren Sie sie, wenn es erforderlich ist (wie in Absender).
Ihr Gesamtdesign ist gut genug, Sie könnten dies jedoch direkt tun:
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; }
}
EmailMessagesollte Von (erforderlich), An (erforderlich), CC (optional), BCC (optional), Betreff und Text als vollständiges Modell der Nachricht enthalten. Der Grund dafür ist, dass es immer als Voraussetzung für jede Nachricht gepaart wird, später wäre es auch einfacher, es auf der Datenbankseite zu implementieren. using IEnumerable<EmailAddress>würde öffnen, um jede Sammlung zu verwenden, die implementiert IEnumerable. Es ist also nicht beschränkt auf List.
Als IEmailProviderE-Mail-Anbieter sollten auch eigene Einstellungen als Voraussetzung enthalten sein. Dies würde es Ihnen erleichtern, jede E-Mail-Nachricht mit eigenen Einstellungen zu haben. Auf diese Weise können Sie ohne zusätzliche Codierung mehrere E-Mails von verschiedenen Anbietern versenden. IOptionswird unabhängig behandelt, ist aber in Wirklichkeit IOptionsfür sich genommen nicht nützlich und würde nur von verwendet EmailProvider. Wenn wir es also zum Vertrag hinzufügen, wird es immer mit der Schnittstelle implementiert, auf diese Weise haben Sie den Anbieter immer dazu gezwungen haben IOptions. Außerdem IOptionssollte es in etwas wie EmailServerSettingoder umbenannt werden EmailProviderSetting, um es auf die Hauptimplementierung zu beziehen. Da dies Einstellungen und keine Optionen sind. VerwendenIOptionsum die optionalen E-Mail-Einstellungen und -Funktionen zu handhaben, die verwaltet werden können, wie das Senden als Text oder HTML, das Deaktivieren / Aktivieren von Anhängen, Bildern usw.
Außerdem benötigen Sie eine Middleware-Klasse, um die Dinge zusammenzufassen, und verwendet SmtpClient. Dies würde Ihnen den Vorteil verschaffen, die Flexibilität Ihrer aktuellen Arbeit zu erhöhen und sie unter einem Dach zusammenzufassen, zusammen mit der Erleichterung der Wiederverwendung von Code (wie MimeMessage, TextPart..etc.), anstatt ihn bei jedem neuen Sender neu zu implementieren. Es würde Ihnen auch die Möglichkeit geben, eine Sammlung zu erstellen, um mehrere Anbieter zu speichern, wenn Sie so weit gehen. Außerdem können Sie neue Anbieter hinzufügen, sie verwalten, Nachrichten bearbeiten und Ihre Arbeit im Rahmen halten.
So stelle ich mir die endgültige Verwendung vor:
Neuen Anbieter anlegen:
// 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
}
Erstellen Sie jetzt eine neue E-Mail-Nachricht und senden Sie sie:
// 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();
}