Предотвращение инъекций IOptions<T>
зависимостей в классах приложений. Это приводит к возникновению проблем, как описано here.
Аналогично, инъекция AppSettings
в проблемы классов, так как это означает, что все классы получают все значения конфигурации, введенные, в то время как они используют только одно или два из этих значений. Это делает эти классы более трудными для тестирования, и становится намного сложнее выяснить, какое значение конфигурации для такого класса действительно требуется. Он также подталкивает проверку конфигурации к вашему приложению, что делает ваше приложение более хрупким; вы узнаете намного позже, когда отсутствует значение конфигурации, вместо того, чтобы узнать, когда приложение запущено.
Класс должен указывать необходимые вещи в своем конструкторе и не должен передавать эти зависимости через другие классы. Это выполняется как для инжектированных компонентов, так и для значений конфигурации. Это означает, что если класс требует определенного значения конфигурации, он должен указать это - и только это значение в своем конструкторе.
Update
«Электронная почта класс», который содержит этот SendVerification()
метод, который вы упоминаете, кажется, как компонент приложения для меня. Поскольку этот класс отправляет фактическую почту, это тот, который требует всех этих настроек конфигурации почты; не контроллер! Поэтому эти настройки следует вводить непосредственно в этот компонент. Но опять же, воздержитесь от инъекций чего-либо общего (например, IOptions<T>
, AppSettings
или IConfiguration
) в этот класс. 1 Будьте как можно более конкретными, как то, что требуется этому классу, и 2. убедитесь, что значения конфигурации читаются при запуске приложения, где вы можете позволить быстро свернуть приложение при запуске приложения.
Так я полагаю, ваш «почтовый класс», чтобы определить абстракцию следующим образом:
public interface IVerificationSender
{
void SendVerification(User user);
}
Это позволяет контроллер принять зависимость от этой абстракции. Обратите внимание: ни один компонент не должен создавать зависимости самих компонентов приложения. Это анти-шаблон, известный как Control Freak (см. this book).
// Controller that depends on the IVerificationSender abstraction
public class HomeController : Controller
{
private readonly IVerificationSender verificationSender;
public HomeController(IVerificationSender verificationSender, ...) {
this.verificationSender = verificationSender;
}
public void SomeAction() {
this.verificationSender.SendVerification(user);
}
}
Теперь у нас есть IVerificationSender
реализация, которая использует почту для отправки сообщений (это ваш «почтовый класс» штуковина).Этот класс сопровождается Parameter Object, который содержит все значения конфигурации, требуемые этим классом (но не более того).
// Settings class for the IVerificationSender implementation
public class SmtpVerificationSenderSettings
{
public string MailHost { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool EnableSsl { get; set; }
// etc
}
public class EmailVerificationSender : IVerificationSender
{
private readonly SmtpVerificationSenderSettings settings;
public EmailVerificationSender(SmtpVerificationSenderSettings settings) {
if (settings == null) throw new ArgumentNullException("settings");
this.settings = settings;
}
public void SendVerification(User user) {
using (var client = new SmtpClient(this.settings.MailHost, 25)) {
smtpClient.EnableSsl = this.settings.EnableSsl;
using (MailMessage mail = new MailMessage()) {
mail.From = new MailAddress("[email protected]", "MyWeb Site");
mail.To.Add(new MailAddress(user.Email));
mail.Body = $"Hi {user.Name}, Welcome to our site.";
client.Send(mail);
}
}
}
}
Используя этот подход, регистрацию как и контроллер EmailVerificationSender
должен быть тривиальным. Вы даже можете использовать эту SmtpVerificationSenderSettings
, как сериализуемая объект, который загружается из файла конфигурации:
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("settubgs.json");
.Build();
var settings = config.GetSection("SmtpVerificationSenderSettings")
.Get<SmtpVerificationSenderSettings>();
// Verify the settings object
if (string.IsNullOrWhiteSpace(settings.MailHost)
throw new ConfigurationErrorsException("MailSettings MailHost missing.");
if (string.IsNullOrWhiteSpace(settings.MailHost)
throw new ConfigurationErrorsException("MailSettings UserName missing.");
// etc
// Register the EmailVerificationSender class
services.AddSingleton<IVerificationSender>(new EmailVerificationSender(settings));
Где settings.json
может выглядеть следующим образом:
{
"SmtpVerificationSenderSettings": {
"MailHost" : "localhost",
"UserName" : "foobar",
// etc
}
}
Спасибо за ваш ответ. В моем примере я прохожу через Appettings (теперь я буду изучать проблему Ioptions). Но в основном это то, что вам нужно, чтобы передать все настройки (например, почтовый хост, usernamer, ssl, ect) для класса электронной почты из Контроллер в классе? В прежние времена я имел обыкновение иметь возможность называть ConfigurationManager непосредственно из класса, поэтому меньшая зависимость от вызывающего класса знать (или извлекать) настройки .. – michael
@micheal, в эти старые дни ваш класс имел такое же количество зависимостей, но они были скрыты, и приложение могло сломаться во время выполнения. – Steven
@michael, если ваш контроллер нуждается в этих настройках, они должны быть введены в этот контроллер. Это не означает, что классу нужен аргумент ctor за значение: вы можете сгруппировать их в объект параметра. – Steven