2016-12-23 3 views
1

Я изучаю IoC & DI с ядром Asp.net. Я установил свой dbcontext и другие классы для ввода в мои контроллеры. В настоящее время мой startup.cs выглядит следующим образом:Как получить доступ к контейнеру Asp.net Core DI от класса

 // Add framework services. 
     services.AddDbContext<ApplicationDbContext>(options => 
      options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 


     services.AddIdentity<ApplicationUser, IdentityRole>(options => 
     { 
      options.Password.RequireDigit = false; 
      options.Password.RequiredLength = 5; 
      options.Password.RequireNonAlphanumeric = false; 
      options.Password.RequireLowercase = false; 
      options.Password.RequireUppercase = false; 
     }).AddEntityFrameworkStores<ApplicationDbContext>() 
      .AddDefaultTokenProviders(); 

     services.AddMvc(); 

     services.Configure<AppSettings>(Configuration.GetSection("AppSettings")); 

Как вы можете видеть, среди прочего, я инъекционный класса AppSettings. У меня нет проблем с доступом к этому классу:

private readonly AppSettings _appSettings; 


    public HomeController(UserManager<ApplicationUser> userManager, 
     ApplicationDbContext dbContext, 
     ViewRender view, 
     IHostingEnvironment env, 
     IOptions<AppSettings> appSettings 
     ) 
    { 

Передача его в конструктор контроллера работает нормально.

Но мне нужно получить доступ к AppSettings в классе и надеялся, что существует статический метод, который я мог бы использовать для ввода класса в любой случайный класс. Это возможно? Или мне нужно ввести его в контроллер и передать его друг другу в класс?

ответ

2

Предотвращение инъекций 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 
    } 
} 
+0

Спасибо за ваш ответ. В моем примере я прохожу через Appettings (теперь я буду изучать проблему Ioptions). Но в основном это то, что вам нужно, чтобы передать все настройки (например, почтовый хост, usernamer, ssl, ect) для класса электронной почты из Контроллер в классе? В прежние времена я имел обыкновение иметь возможность называть ConfigurationManager непосредственно из класса, поэтому меньшая зависимость от вызывающего класса знать (или извлекать) настройки .. – michael

+0

@micheal, в эти старые дни ваш класс имел такое же количество зависимостей, но они были скрыты, и приложение могло сломаться во время выполнения. – Steven

+0

@michael, если ваш контроллер нуждается в этих настройках, они должны быть введены в этот контроллер. Это не означает, что классу нужен аргумент ctor за значение: вы можете сгруппировать их в объект параметра. – Steven