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"));
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
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.
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");
}
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.
Related
I'm new to ASP.NET Core and I'm trying to solve this problem for a week now.
I have a solution with two projects.
And when I start the porject the browser just says:
InvalidOperationException: Unable to resolve service for type 'TSM_Programm.Data.TSMContext' while attempting to activate 'TSM_Programm.Controllers.ResourcesController'.
The first part of the solution is my API-Layer that passes data to a user (currently via postman).
The second project is my Data Access Layer.
This Layer contains several Controllers, all of them using the same constructor, which is the following:
public TSMContext _context;
public ResourcesController(TSMContext context)
{
_context = context;
}
The TSMContext Class is the following:
namespace TSM_Programm.Data
{
public class TSMContext : DbContext
{
public TSMContext(DbContextOptions<TSMContext> options)
: base(options)
{
}
public DbSet<Resource> Resources { get; set; }
public DbSet<Parameter> Parameters { get; set; }
public DbSet<ResourceToParameter> ResourceToParameters { get; set; }
public DbSet<Reservation> Reservations { get; set; }
}
So far so god, but when I am trying to start the program the controllerof the API-Layer does not seem to be able to handle the constructor.
This is my API-Conrtoller:
namespace TSM_API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class APIController : ControllerBase //Base Class without View Support
{
//Troublemaker
public ResourcesController _resources;
public ParametersController _parameters;
public ReservationsController _reservations;
public APIController(ResourcesController resources, ParametersController parameters, ReservationsController reservations)
{
_resources = resources;
_parameters = parameters;
_reservations = reservations;
}
//Function to check if controller works
//GET: api/API
[HttpGet]
public IEnumerable<string> Get()
{
// ResourcesController controller = new ResourcesController();
return new string[] { "value1", "value2" };
}
The API-Controller was not able to use its own constructors, that's why I changed the Startup.cs.
namespace TSM_API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddMvc().AddApplicationPart(typeof(ResourcesController).Assembly).AddControllersAsServices();
services.AddMvc().AddApplicationPart(typeof(ParametersController).Assembly).AddControllersAsServices();
services.AddMvc().AddApplicationPart(typeof(ReservationsController).Assembly).AddControllersAsServices();
services.AddMvc().AddApplicationPart(typeof(TSMContext).Assembly).AddControllersAsServices();
}
I'm simply out of ideas on how to solve the problem, since I can't add the TSMContext class a service.
Any idea how to solve it?
Thank you.
I see you have not registered your dbcontext as a dependency injection. Your issue might be due to ResourceController trying to access _context as a DI but it is not registered. To use the context as a dependency injection, register it in the startup.cs as following.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TSMContext>(options => options.UseSqlServer(Configuration.GetConnectionString("YOUR_CONNECTION_STRING")));
//If you have any services that should be used as DI, then they also must be registered as like this
services.AddScoped<Interface, Class>(); //Interface refer to the service interface while class is the actual service you will use.
}
After I've read this article about dependency injection Here I still do not have a clear understanding on how to read the appsetting in other than a controller classes.
Lets say for instance I have a helper class with a bunch of static methods that I'm planning to use, I do not create an instance of this class, how do I read setting values to use inside the methods of this class?
I used to create helper class to read data from appsettings.config in one of my applications:
public static class ConfigValueProvider
{
private static readonly IConfigurationRoot Configuration;
static ConfigValueProvider()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
Configuration = builder.Build();
}
public static string Get(string name)
{
return Configuration[name];
}
}
However later I reviewed my application to get away from static methods which depends on application config in order to make my application testable.
You should use services.Configure as below:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<JSonAsClass>(Configuration.GetSection("MySectionName"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
then you can inject JSonAsClass inside any class you want to use it:
private JSonAsClass jSonAsClass;
public MailService(IOptions<JSonAsClass> jSonAsClass)
{
this.jSonAsClass = jSonAsClass.Value;
}
I can't get ASP.NET 5 (i.e. Core 1.0) to work with immutable configuration classes.
Let me demonstrate first using a mutable class.
config.json
{
"Name": "Lou",
"Age": 30
}
Config.cs (mutable)
public class Config
{
public string Name { get; set; }
public int Age { get; set; }
}
Registration in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var configBuilder = new ConfigurationBuilder()
.AddJsonFile("config.json");
var config = configBuilder.Build();
services.Configure<Config>(config);
services.AddMvc();
}
The code above works well and the values of Config.Name and Config.Age are as expected.
But once I change Config's property setters to private, they are no longer set by the configuration builder:
public class Config
{
public string Name { get; private set; } // == null
public int Age { get; private set; } // == 0
}
I would have expected ASP.NET 5 to use reflection and set the properties like it does with ASP.NET 4, instead of simply ignoring them. Am I missing something?
I created a sample project to demonstrate: https://github.com/johnnyoshika/mvc6-immutable-config
The master branch uses a mutable Config class while the immutable-config branch uses an immutable Config class.
The ConfigurationBinder object doesn't set private properties: https://github.com/aspnet/Configuration/blob/ba1d9b4cbc363db9318f201f08fbfecc72e7922b/src/Microsoft.Extensions.Configuration.Binder/ConfigurationBinder.cs#L64-L82
Here's an issue tracking that: https://github.com/aspnet/Configuration/issues/394
One way to workaround this is to create an interface with getters only that's implemented by your Config code. Then, you can use that in your code.
I am playing with ASP.NET 5. I am trying to understand the new configuration model. I have read several articles. However, I am still unsuccessful in loading a configuration setting. My config.json file looks like this:
{
"App" : {
"Info" : {
"Version":"1.0.0",
"ReleaseDate":"03-15-2015"
}
}
}
My Startup.cs file looks like this:
public class Startup
{
public IConfiguration Configuration { get; private set; }
public Startup()
{
Configuration = new Configuration()
.AddJsonFile("config.json");
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index" });
});
app.UseMvc();
app.UseWelcomePage();
}
}
In one of my controllers, I have the following
MyController.cs
using System;
using Microsoft.AspNet.Mvc;
namespace MyOrg.MyApp
{
public class MyController : Controller
{
[HttpGet()]
public ActionResult Index()
{
var version = Configuration.Get("App:Info:Version");
return new HttpStatusCodeResult(200);
}
}
}
When I start the app, I get an error that says:
error CS0103: The name 'Configuration' does not exist in the current context
at Microsoft.Framework.Runtime.Roslyn.RoslynProjectReference.Load(IAssemblyLo
adContext loadContext)
at Microsoft.Framework.Runtime.Loader.ProjectAssemblyLoader.Load(String name,
IAssemblyLoadContext loadContext)
at Microsoft.Framework.Runtime.Loader.ProjectAssemblyLoader.Load(String name)
at kre.host.LoaderContainer.Load(String name)
at kre.hosting.RuntimeBootstrapper.<>c__DisplayClass6_0.<ExecuteAsync>b__4(As
semblyName assemblyName)
at kre.hosting.RuntimeBootstrapper.<>c__DisplayClass6_0.<ExecuteAsync>b__7(Ob
ject sender, ResolveEventArgs a)
at System.AppDomain.OnAssemblyResolveEvent(RuntimeAssembly assembly, String assemblyFullName)
What am I doing wrong? I feel like I've followed the examples I've seen. Yet, I can figure out what I'm doing wrong.
Clearly you want to access Configuration property in your Startup class. And the error method says it doesn't know what Configuration is. So you need a using statement or a fully qualified name. Also, you should avoid naming things the same thing as stuff found in the framework. Your Startup class has a Configuration property, but it also tries to use the Configuration class from Microsoft.Framework.ConfigurationModel. How confusing is that?
Your Configure() method in Startup needs a using statement or fully qualified name so it knows what the Configuration class is.
using Microsoft.Framework.ConfigurationModel; //at the top of your class
Configuration = new Configuration(); //later in the code, we can access without fully qualifying name
or
Configuration = new Microsoft.Framework.ConfigurationModel.Configuration();
In your controller, you may have a similar issue. Replace MyOrg.MyApp.Startup in the example below with whatever the namespace is for your Startup class.
using MyOrg.MyApp.Startup //at the top of your class
Startup.Configuration.Get("App:Info:Version"); //later in the code, we can access without fully qualifying name
or
MyOrg.MyApp.Startup.Startup.Configuration.Get("App:Info:Version");
Better way of doing things
That should be enough to get you started. However, accessing the Startup class to retrieve your configuration isn't ideal, because now your controller's action methods depend on having the Startup class there. That's not very unit testable. Ideally your controllers should be isolated from each other. You should define some sort of interface to hold the configuration info you want, then have the controller depend on that interface. When you're in your site, you'll respond with a class specific to the site's configuration. When unit testing, you can have tight control over the test values by using a different class.
interface ISiteConfig
{
string Version {get; set;}
DateTime ReleaseDate {get; set;}
}
public class SiteConfig : ISiteConfig
{
public string Version {get; set;}
public DateTime ReleaseDate {get; set;}
public SiteConfig()
{
var c = new Configuration()
.AddJsonFile("config.json");
Version = c.Get("App:Info:Version");
ReleaseDate = c.Get("App:Info:ReleaseDate"); //may need to parse here
}
}
public class TestConfig : ISiteConfig
{
public string Version {get; set;}
public DateTime ReleaseDate {get; set;}
public TestConfig(string version, DateTime releaseDate)
{
Version = version;
ReleaseDate = releaseDate;
}
}
Then you'd use Dependency Injection to inject instances of your configuration into the Controller.
public class MyController : Controller
{
private readonly ISiteConfig Config;
public MyController(ISiteConfig config)
{
Config = config;
}
[HttpGet()]
public HttpStatusCodeResult Index()
{
var version = Config.Version;
return new HttpStatusCodeResult(200);
}
}
public class Startup
{
public void Configure(IBuilder app)
{
...
app.UseServices(services =>
{
...
// Set up the dependencies
services.AddTransient<ISiteConfig, SiteConfig>();
...
});
...
}
}
Now you can more easily unit test your action methods, because your unit tests can use the TestConfig class while the site can use the SiteConfig class. And also if you want to change how your configuration is done, you don't have to replace strings in a bunch of different places. You'll have one class where you do so, the rest is strongly typed and easy to change without blowing up your application.
Your unit test might look like this:
//Arrange
var testConfig = new TestConfig("1.0", DateTime.Now );
var controller = new MyController(testConfig );
//Act
var response = controller.Index();
//Assert
Assert.AreEqual(200, response.StatusCode);
As of Beta 5 the accepted answer is no longer correct. There is no longer a Get method on IConfiguration. Also the way of constructing the configuration object is also changed.
The following code works on Beta 7:
// showing using statements here since this is new from Beta 5
using Microsoft.Dnx.Runtime; // renamed was Microsoft.Framework.Runtime
using Microsoft.Framework.Configuration; // renamed was Microsoft.Framework.ConfigurationModel
// other using statements here
// Startup constructor. Note: now takes IApplicationEnvironment
// this is required in order to get base path
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
// Setup configuration sources.
var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
.AddJsonFile("config.json")
.AddJsonFile("dbconfig.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
// property to hold configuration object created in constructor
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
// this will bind to an IOptions<AppSettings> instance
// where AppSettings is a class you define that has a set of
// properties that match your configuration section loaded from the
// json file
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// here I am loading a connection string from a json file and passing into an
// new EF 6.x DB Context class
services.AddInstance<TalentAgencyContainer>(new TalentAgencyContainer(Configuration["ConnectionStrings:TalentAgencyContainer"]));
// Add MVC services to the services container.
services.AddMvc();
}
I have a facade in a library that exposes some complex functionality through a simple interface. My question is how do I do dependency injection for the internal types used in the facade. Let's say my C# library code looks like -
public class XYZfacade:IFacade
{
[Dependency]
internal IType1 type1
{
get;
set;
}
[Dependency]
internal IType2 type2
{
get;
set;
}
public string SomeFunction()
{
return type1.someString();
}
}
internal class TypeA
{
....
}
internal class TypeB
{
....
}
And my website code is like -
IUnityContainer container = new UnityContainer();
container.RegisterType<IType1, TypeA>();
container.RegisterType<IType2, TypeB>();
container.RegisterType<IFacade, XYZFacade>();
...
...
IFacade facade = container.Resolve<IFacade>();
Here facade.SomeFunction() throws an exception because facade.type1 and facade.type2 are null. Any help is appreciated.
Injecting internal classes is not a recommended practice.
I'd create a public factory class in the assembly which the internal implementations are declared which can be used to instantiate those types:
public class FactoryClass
{
public IType1 FirstDependency
{
get
{
return new Type1();
}
}
public IType2 SecondDependency
{
get
{
return new Type2();
}
}
}
And the dependency in XYZFacade would be with the FactoryClass class:
public class XYZfacade:IFacade
{
[Dependency]
public FactoryClass Factory
{
get;
set;
}
}
If you want to make it testable create an interface for the FactoryClass.
If the container creation code is outside the assembly of the internal types, Unity can't see and create them and thus can't inject the dependecies.