Expéditeur d'e-mail abstrait avec fournisseurs de contenu
J'ai créé un expéditeur d'e-mail avec des fournisseurs pour le contenu de l'e-mail, qui sera modifié en fonction du type d'e-mail. J'ai besoin d'aide pour l'améliorer.
Ces deux modèles sont utilisés pour envoyer
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; }
}
Le fournisseur de contenu fournit toutes les informations de l'e-mail (sujet, corps, À et Cc)
public interface IEmailContentProvider
{
EmailMessage Message { get; }
}
Ensuite, nous avons l'expéditeur d'e-mail abstrait IEmailSender
qui a une seule méthode Send
qui utilise un IEmailContentProvider
paramètre pour obtenir les informations d'e-mail
interface IEmailSender
{
Task Send(IEmailContentProvider provider);
}
J'ai un exemple pour le fournisseur de contenuWelcomEmailProvider
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} }
};
}
}
La IEmailSender
mise en œuvre :
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);
}
}
Et voici, comment l'utiliser et envoyer un 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"));
}
}
Réponses
public EmailMessage()
{
ToAddresses = new List<EmailAddress>();
CcAddresses = new List<EmailAddress>();
}
Pas nécessaire, je peux comprendre si vous venez de lancer le ToAddress
, cependant, lancer les listes comme celle-ci peut consommer beaucoup de mémoire (imaginez que vous avez une grande quantité d' EmailMessage
instances! Donc, je suggérerais de les garder comme nulles et d'utiliser null
la validation pour forcer les initier quand c'est nécessaire (comme dans l'expéditeur).
votre conception globale est assez bonne, cependant, vous pouvez le faire directement :
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
doit contenir From (obligatoire), To (obligatoire), CC (facultatif), BCC (facultatif), Objet et Corps comme modèle complet du message. La raison derrière cela est qu'il sera toujours associé comme exigence à tout message, plus tard, il serait également plus facile à implémenter du côté de la base de données. using IEnumerable<EmailAddress>
s'ouvrirait pour utiliser n'importe quelle collection qui implémente IEnumerable
. Donc, ce n'est pas limité à List
.
Pour IEmailProvider
En tant que fournisseur de messagerie, il doit également contenir ses propres paramètres comme exigence. Cela faciliterait les choses pour vous, d'avoir chaque message électronique avec ses propres paramètres. De cette façon, vous pouvez envoyer plusieurs e-mails de différents fournisseurs sans codage supplémentaire. IOptions
est traité indépendamment, mais en réalité, IOptions
ne sera pas utile en soi, et il ne serait utilisé que par EmailProvider
, donc si nous l'ajoutons au contrat, il sera toujours implémenté avec l'interface, de cette façon vous avez forcé le fournisseur à toujours avoir IOptions
. En outre, IOptions
devrait être renommé en quelque chose comme EmailServerSetting
ou EmailProviderSetting
pour le relier à l'implémentation principale. Comme ce sont des paramètres et non des options. UtilisationIOptions
pour gérer les paramètres facultatifs de messagerie et les fonctionnalités qui peuvent être gérées comme l'envoi de texte ou html, désactiver/activer les pièces jointes, les images, etc.
En outre, vous avez besoin d'une classe intermédiaire pour conclure et utilise SmtpClient
. Cela vous donnerait l'avantage d'augmenter la flexibilité de votre travail actuel et de les regrouper sous un même toit tout en facilitant la réutilisation du code (tel que MimeMessage
, TextPart
..etc.) au lieu de le réimplémenter sur chaque nouvel expéditeur. Cela vous donnerait également la possibilité de créer une collection pour stocker plusieurs fournisseurs si vous allez aussi loin. De plus, vous pourrez ajouter de nouveaux fournisseurs, les gérer, gérer les messages et conserver la portée de votre travail.
Voici comment j'imagine l'utilisation finale :
Création d'un nouveau fournisseur :
// 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
}
maintenant, créer un nouveau message électronique et l'envoyer :
// 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();
}