ASP.NET 5 DI app setting outside controller

北城以北 提交于 2019-12-02 00:04:15

问题


I can DI app setting in the controller like this

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

But how to DI that in my custom class like this

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

my register in Startup.cs is

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

回答1:


The "proper" way

Register your custom class in the DI, the same way you register other dependencies in ConfigureServices method, for example:

services.AddTransient<PermissionFactory>();

(Instead of AddTransient, you can use AddScoped, or any other lifetime that you need)

Then add this dependency to the constructor of your controller:

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

Now, DI knows about PermissionFactory, can instantiate it and will inject it into your controller.

If you want to use PermissionFactory in Configure method, just add it to it's parameter list:

Configure(IApplicationBuilder app, PermissionFactory prov)

Aspnet will do it's magic and inject the class there.

The "nasty" way

If you want to instantiate PermissionFactory somewhere deep in your code, you can also do it in a little nasty way - store reference to IServiceProvider in Startup class:

internal static IServiceProvider ServiceProvider { get;set; }

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

Now you can access it like this:

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

Again, DI will take care of injecting IOptions<AppSettings> into PermissionFactory.

Asp.Net 5 Docs in Dependency Injection




回答2:


I recommend not passing AppSettings. A class shouldn't depend on something vague - it should depend on exactly what it needs, or close to it. ASP.NET Core makes it easier to move away from the old pattern of depending on AppSettings. If your class depends on AppSettings then you can't really see from the constructor what it depends on. It could depend on any key. If it depends on a more specific interface then its dependency is clearer, more explicit, and you can mock that interface when unit testing.

You can create an interface with the specific settings that your class needs (or something less specific but not too broad) and a class that implements it - for example,

    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; }
    }

Then add a .json file, fooSettings.json:

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

Then, in Startup() (in Startup.cs) where we specify what goes into our Configuration, add fooSettings.json:

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

Finally, in ConfigureServices() (also in Startup.cs) tell it to load an instance of FooSettings, cast it as IFooSettings (so the properties appear read-only) and supply that single instance for all dependencies on IFooSettings:

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

Now your class - controller, filter, or anything else created by the DI container - can have a dependency on IFooSettings and it will be supplied from the .json file. But you can mock IFooSettings for unit testing.

Original blog post - it's mine so I'm not plagiarizing.




回答3:


You can do dependency injection in your non-controller classes as well.

In your startup class,

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);     
  }
}

Now in your custom class, Have the constructor accept an implementation of 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");
}



回答4:


If you want to DI to action filter reference to Action filters, service filters and type filters in ASP.NET 5 and MVC 6 service filter part.



来源:https://stackoverflow.com/questions/36904174/asp-net-5-di-app-setting-outside-controller

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!