Настройка приложения ASP.NET 5 DI вне контроллера

Я могу настроить приложение DI в контроллере, как это

 private IOptions<AppSettings> appSettings;
 public CompanyInfoController(IOptions<AppSettings> appSettings)
 {
     this.appSettings = appSettings;
 }

Но как сделать это в моем пользовательском классе, как это

  private IOptions<AppSettings> appSettings;
  public PermissionFactory(IOptions<AppSettings> appSetting)
  {
      this.appSettings = appSettings;
  }

моя регистрация в Startup.cs

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

person MichaelMao    schedule 28.04.2016    source источник


Ответы (4)


«Правильный» способ

Зарегистрируйте свой пользовательский класс в DI точно так же, как вы регистрируете другие зависимости в методе ConfigureServices, например:

services.AddTransient<PermissionFactory>();

(Вместо AddTransient вы можете использовать AddScoped или любое другое время жизни, которое вам нужно)

Затем добавьте эту зависимость в конструктор вашего контроллера:

public CompanyInfoController(IOptions<AppSettings> appSettings, PermissionFactory permFact)

Теперь DI знает о PermissionFactory, может создать его экземпляр и внедрит его в ваш контроллер.

Если вы хотите использовать PermissionFactory в методе Configure, просто добавьте его в список параметров:

Configure(IApplicationBuilder app, PermissionFactory prov)

Aspnet сделает свое волшебство и внедрит туда класс.

«Гнусный» способ

Если вы хотите создать экземпляр PermissionFactory где-то глубоко в своем коде, вы также можете сделать это немного неприятным способом — сохранить ссылку на IServiceProvider в классе Startup:

internal static IServiceProvider ServiceProvider { get;set; }

Configure(IApplicationBuilder app, IServiceProvider prov) {
   ServiceProvider = prov;
   ...
}

Теперь вы можете получить к нему доступ следующим образом:

var factory = Startup.ServiceProvider.GetService<PermissionFactory>();

Опять же, DI позаботится о вставке IOptions<AppSettings> в PermissionFactory.

Документы Asp.Net 5 по внедрению зависимостей

person qbik    schedule 28.04.2016
comment
Я хочу внедрить AppSetting в PermissionFactory, а не добавлять PermissionFactory в CompanyInfoController. - person MichaelMao; 28.04.2016
comment
Вопрос в том, где вы хотите получить доступ к этому классу PermissionFactory. Если какой-то класс B использует его, вы должны добавить PermissionFactory в конструктор B и так далее, пока не достигнете некоторой точки входа, которая, вероятно, является контроллером. - person qbik; 28.04.2016
comment
но... запись не является контроллером... поэтому я говорю внешний контроллер - person MichaelMao; 28.04.2016
comment
Важно то, что класс вне контроллера также разрешается контейнером внедрения зависимостей. Таким образом, если у вас есть контроллер, и контроллер зависит от A, а A зависит от B и C, а C зависит от D, E, F и т. д., контейнер введет необходимые зависимости во все из них при разрешении проблемы. контроллер. Это наиболее вероятная точка входа, но она также может быть и фильтром. - person Scott Hannen; 28.04.2016
comment
Это фильтр действий - person MichaelMao; 28.04.2016
comment
О var factory = Startup.ServiceProvider.prov.GetService<PermissionFactory>(); Вы проверяете это? у меня не работает событие удалить .prov и изменить GetService - person MichaelMao; 28.04.2016
comment
Я отмечаю этот ответ, но вам нужно проверить код неприятным образом, и если кому-то нужна информация о DI в действии, фильтр может ссылаться на Внедрение зависимостей в атрибуты: не делайте этого! - person MichaelMao; 28.04.2016
comment
код неприятным образом не работает. Неуниверсальный метод IServiceProvider.GetService(Type) нельзя использовать с аргументами типа. - person vlukham; 09.10.2016

Я рекомендую не проходить AppSettings. Класс не должен зависеть от чего-то расплывчатого — он должен зависеть именно от того, что ему нужно, или близко к этому. ASP.NET Core упрощает отказ от старого шаблона зависимости от AppSettings. Если ваш класс зависит от AppSettings, то вы не можете увидеть из конструктора, от чего он зависит. Это может зависеть от любого ключа. Если он зависит от более конкретного интерфейса, то его зависимость более четкая, более явная, и вы можете имитировать этот интерфейс при модульном тестировании.

Вы можете создать интерфейс с конкретными настройками, которые нужны вашему классу (или что-то менее конкретное, но не слишком широкое) и класс, который его реализует, например,

    public interface IFooSettings
    {
        string Name { get; }
        IEnumerable Foos { get; }
    }

    public interface IFoo
    {
        string Color { get;  }
        double BarUnits { get;  }
    }

    public class FooSettings : IFooSettings
    {
        public string Name { get; set; }
        public List<Foo> FooList { get; set; }

        public IEnumerable Foos
        {
            get
            {
                if (FooList == null) FooList = new List<Foo>();
                return FooList.Cast<IFoo>();
            }
        }
    }

    public class Foo : IFoo
    {
        public string Color { get; set; }
        public double BarUnits { get; set; }
    }

Затем добавьте файл .json, fooSettings.json:

    {
      "FooSettings": {
        "Name": "MyFooSettings",
        "FooList": [
          {
            "Color": "Red",
            "BarUnits": "1.5"
          },      {
            "Color": "Blue",
            "BarUnits": "3.14159'"
          },      {
            "Color": "Green",
            "BarUnits": "-0.99999"
          }
        ]
      }
    }

Затем в Startup() (в Startup.cs), где мы указываем, что входит в наш Configuration, добавляем fooSettings.json:

    var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
        .AddJsonFile("config.json")
        .AddJsonFile($"config.{env.EnvironmentName}.json", optional: true)
        .AddJsonFile("fooSettings.json");

Наконец, в ConfigureServices() (также в Startup.cs) скажите ему загрузить экземпляр FooSettings, преобразовать его как IFooSettings (чтобы свойства отображались только для чтения) и предоставить этот единственный экземпляр для всех зависимостей от IFooSettings:

    var fooSettings = (IFooSettings)ConfigurationBinder.Bind<FooSettings>(
        Configuration.GetConfigurationSection("FooSettings"));
    services.AddInstance(typeof (IFooSettings), fooSettings);

Теперь ваш класс — контроллер, фильтр или что-то еще, созданное контейнером DI — может иметь зависимость от IFooSettings и будет поставляться из файла .json. Но вы можете издеваться над IFooSettings для модульного тестирования.

Исходный пост в блоге - это мое, так что я не занимаюсь плагиатом.

person Scott Hannen    schedule 28.04.2016

Вы также можете выполнять внедрение зависимостей в свои классы, не являющиеся контроллерами.

В вашем startup классе

public class Startup
{
  public IConfigurationRoot Configuration { get; set; }

  public Startup(IHostingEnvironment env)
  {
        // Set up configuration sources.
     var builder = new ConfigurationBuilder()
             .AddJsonFile("appsettings.json")
             .AddEnvironmentVariables();
     Configuration = builder.Build();
  }
  public void ConfigureServices(IServiceCollection services)
  {
     // register other dependencies also here
     services.AddInstance<IConfiguration>(Configuration);     
  }
}

Теперь в вашем пользовательском классе попросите конструктор принять реализацию IConfiguration

private IConfiguration configuration;
public PermissionFactory(IConfiguration configuration)
{
  this.configuration = configuration;
}
public void SomeMethod()
{
  var someSection = this.configuration.GetSection("SomeSection");
  var someValue= this.configuration.Get<string>("YourItem:SubItem");
}
person Shyju    schedule 28.04.2016
comment
так что... если я изменю имя раздела в project.json, мне нужно проверить проект отверстия, который использует этот раздел? - person MichaelMao; 28.04.2016
comment
Итак, если я хочу создать экземпляр PermissionFactory где-то еще, кроме контроллера, я могу просто вызвать var pf = new PermissionFactory(), и он будет работать автоматически? Что произойдет, если я также захочу передать ручные параметры в этот конструктор? - person Keab42; 23.08.2017
comment
Не new экземпляр вообще! Используйте DI везде в вашем коде. - person Shyju; 23.08.2017
comment
Что вы имеете в виду, не new экземпляр? класс нельзя использовать без инициализации экземпляра. - person John_J; 08.05.2018

Если вы хотите, чтобы DI активировал фильтр, обратитесь к Фильтры действий, фильтры служб и фильтры типов в части фильтра служб ASP.NET 5 и MVC 6.

person MichaelMao    schedule 28.04.2016