I'm creating a service that requires some config parameters and a logger. Here is the constructor for my service:
public StorageProvider(string directory, ILogger<StorageProvider> logger)
I just added the logger. I used to initalize it like this in my startup.cs:
services.AddSingleton<IStorageProvider>(
new StorageProvider(Configuration["TempStorage.Path"]));
The directory parameter comes from the config file, and the logger gets DI'ed. How do I setup my IStorageProvider?
You should do the following:
Wrap the configuration value TempStorage:Path into its own configuration class, e.g. StorageProviderSettings.
Let StorageProvider depend upon that new configuration class.
Register that configuration class as singleton into the ASP.NET configuration system.
Example:
public sealed class StorageProviderSettings
{
public readonly string TempStoragePath;
public StorageProviderSettings(string tempStoragePath)
{
if (string.IsNullOrWhiteSpace(tempStoragePath))
throw new ArgumentException(nameof(tempStoragePath));
this.TempStoragePath = tempStoragePath;
}
}
public sealed class StorageProvider : IStorageProvider
{
public StorageProvider(
StorageProviderSettings settings, ILogger<StorageProvider> logger)
{
// ...
}
}
// Registration
services.AddSingleton(new StorageProviderSettings(Configuration["TempStorage.Path"]));
services.AddSingleton<IStorageProvider, StorageProvider>();
Use the Options pattern as Tratcher suggests in a comment. Read more in the official docs on Configuration.
Basically you define a class to be hold the value you need:
public class StorageProviderOptions
{
public string TempStoragePath { get; set; }
}
Then in ConfigureServices you register the type:
services.Configure<StorageProviderOptions>();
In your code, you request IOptions<StorageProviderOptions> and set this to an instance of StorageProviderOptions:
public class SomeController
{
private readonly StorageProviderOptions _options;
public SomeController(IOptions<StorageProviderOptions> options)
{
_options = options.Value;
}
}
Finally, make sure you have an element in your configuration source that matches the TempStoragePath name. Alternately, you can register the option in ConfigureServices using code:
services.Configure<ServiceProviderOptions>(o => o.TempStoragePath = "temp");
Related
I have a series of services I am configuring in my application and one of those services require a base URL to a specific route so I can create links based on it. So if we have:
My Controller
[Route("api/v1/fancy")]
public class FancyController {
[HttpPost]
[Route("{fancyID}")]
public async Task<IActionResult> SubmitFancy(string fancyID){
// Do fancy stuff
}
}
My business class
public class Business {
private string _baseUrl;
public Business(string baseUrl){
_baseUrl = baseUrl
}
}
My Startup.cs
...
public void ConfigureServices(IServiceCollection services) {
services.AddScoped<Business>(provider => {
Business business = new Business("http://someweb.com/api/v1/fancy"); //TODO:REMOVE Hard Coded
return business;
}
services.AddRazorPages();
}
...
I have tried to use UrlHelper by adding a few more scoped services for IActionContextAccessor and IUrlHelperFactory, but I am getting null on ActionLink and RouteUrl methods, and I am not sure why.
Any ideas as to how I would go about solving this issue?
Please let me know if you need more clarification.
Thank you very much.
Inject a LinkGenerator & IHttpContextAccessor into your service;
public class Business {
private readonly LinkGenerator generator;
private readonly IHttpContextAccessor accessor;
public Business (LinkGenerator generator, IHttpContextAccessor accessor){...}
public void Foo(){
var context = accessor.HttpContext;
var link = generator.GetUriByAction(
context,
"SubmitFancy",
"Fancy",
new { fancyID="..." });
}
}
services.AddScoped<Business>();
You can use LinkGenerator without a reference to a HttpContext, but you'd need to supply the host, scheme and pathBase from somewhere else. Either from configuration, or perhaps by implementing middleware to capture them from the first request.
You can't use a string for an attribute routing. You need a CONSTANT string. Constants are immutable values which are known at compile time and do not change for the life of the program.
But if you need a route to use in ajax or httpclient, it takes several steps to get a string from appsettings.
create AppUrl section in appsettings.json
"AppUrl": {
"BusinessUrl": "http//..",
.... another urls if needed
},
2.Create class for this section
public class AppUrlSettings
{
public string BusinessUrl{ get; set; }
....another urls
}
configure settings in startup
services.Configure<AppUrlSettings>(Configuration.GetSection("AppUrl"));
now you can use them like this
public class MyClass
{
private readonly IOptions<AppUrlSettings> _appUrls;
public MyClass (IOptions<AppUrlSettings> appUrls)
{
_appUrls = appUrls;
}
public string GetBusinessUrl()
{
return _appUrls.Value.BussinesUrl;
}
}
The problem to solve:
There is a list of settings lets say:
{
"Kind1":
{"attr1":"val11"},
{"attr2":"val12"},
"Kind2":
{"attr1":"val21"},
{"attr2":"val22"},
}
and a consumer class (controller) in .NET Core 2.1, needs to access the above configuration to use Kind1 or Kind2.
Supposing corresponding class is already defined in C#:
public class KindSetting
{
public string attr1{get;set;}
public string attr2{get;set;}
}
Now what is the best way of inject the configuration into the consumer object.
Is there a way to inject an IConfiguration instance into the consumer object and use it like this?:
KindSetting kindSetting =_configuration.GetValue<KindSetting>(kindSettingKey);
Is there any better approach to fulfill the above requirement?
In the startup.cs file, in the ConfigureServices method you can do a configuration. Sample code below:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//Need to add following lines
services.Configure<KindSetting>(Configuration.GetSection("Kind1"));
}
After adding into services, you can inject this configuration in your class, like follow:
public class HomeController : Controller
{
private readonly IOptions<KindSetting> _KindSetting;
public HomeController(IOptions<KindSetting> KindSetting)
{
_KindSetting = KindSetting
}
public void myFunction()
{
var mysetting = _KindSetting.Value.attr1
}
}
I used the following approach, however I am not sure be the best
in startup class configure method:
services.Configure<List<KindSetting>>(Configuration.GetSection("KindSettingList"));
and in consumer object side:
public ConsumerController(IOptions<List<KindSetting>> kindSettingsListAccessor,...)
After adding services.Configure<KindSettings>, you can inject configuration via DI by adding to your constructor.
IOptionsSnapshot<KindSettings> kindSettingsConfiguration
or
IOptions<KindSettings> kindSettingsConfiguration
The difference is, IOptionsSnapshot will reflect live changes in your config file, while IOptions is for singleton use.
Edit after comment:
Lets say your configuration file looks like this:
{
"Kind1":
{"attr1":"val11"},
{"attr2":"val12"},
"Kind2":
{"attr1":"val21"},
{"attr2":"val22"},
}
To successfully bind this, you would need two configuration classes
public class Kind1Configuration
{
public string Attr1 { get; set; }
public string Attr2 { get; set; }
}
public class Kind2Configuration
{
public string Attr1 { get; set; }
public string Attr2 { get; set; }
}
And as previously stated, to connect the dots just add
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Kind1Configuration>(Configuration.GetSection("Kind1"));
services.Configure<Kind2Configuration>(Configuration.GetSection("Kind2"));
}
To use this in a controller, add your IOptions to the constructor
public class TestController(IOptionsSnapshot<Kind1Configuration> kindSettingsConfiguration)
{
Kind1Configuration configuration = kindSettingsConfiguration.Value;
}
Hope it helps.
At this point I'm injecting things into my Controllers with ease, in some cases building my own ResolverServices class. Life is good.
What I cannot figure out how to do is get the framework to automatically inject into non-controller classes. What does work is having the framework automatically inject into my controller IOptions, which is effectively the configuration for my project:
public class MessageCenterController : Controller
{
private readonly MyOptions _options;
public MessageCenterController(IOptions<MyOptions> options)
{
_options = options.Value;
}
}
I'm thinking whether I can do the same for for my own classes. I assume I'm close when I mimic the controller, like this:
public class MyHelper
{
private readonly ProfileOptions _options;
public MyHelper(IOptions<ProfileOptions> options)
{
_options = options.Value;
}
public bool CheckIt()
{
return _options.SomeBoolValue;
}
}
I think where I'm failing is when I call it like this:
public void DoSomething()
{
var helper = new MyHelper(??????);
if (helper.CheckIt())
{
// Do Something
}
}
The problem I have tracking this down is practically everything that talks about DI is talking about it at the controller level. I tried hunting down where it happens in the Controller object source code, but it gets kinda crazy in there.
I do know I can manually create an instance of IOptions and pass it to the MyHelper constructor, but it seems like I should be able to get the framework do that since it works for Controllers.
Below is a working example of using DI without anything that involves MVC Controllers. This is what I needed to do to understand the process, so maybe it will help somebody else.
The ShoppingCart object gets, via DI, an instance of INotifier (which notifies the customer of their order.)
using Microsoft.Extensions.DependencyInjection;
using System;
namespace DiSample
{
// STEP 1: Define an interface.
/// <summary>
/// Defines how a user is notified.
/// </summary>
public interface INotifier
{
void Send(string from, string to, string subject, string body);
}
// STEP 2: Implement the interface
/// <summary>
/// Implementation of INotifier that notifies users by email.
/// </summary>
public class EmailNotifier : INotifier
{
public void Send(string from, string to, string subject, string body)
{
// TODO: Connect to something that will send an email.
}
}
// STEP 3: Create a class that requires an implementation of the interface.
public class ShoppingCart
{
INotifier _notifier;
public ShoppingCart(INotifier notifier)
{
_notifier = notifier;
}
public void PlaceOrder(string customerEmail, string orderInfo)
{
_notifier.Send("admin#store.com", customerEmail, $"Order Placed", $"Thank you for your order of {orderInfo}");
}
}
public class Program
{
// STEP 4: Create console app to setup DI
static void Main(string[] args)
{
// create service collection
var serviceCollection = new ServiceCollection();
// ConfigureServices(serviceCollection)
serviceCollection.AddTransient<INotifier, EmailNotifier>();
// create service provider
var serviceProvider = serviceCollection.BuildServiceProvider();
// This is where DI magic happens:
var myCart = ActivatorUtilities.CreateInstance<ShoppingCart>(serviceProvider);
myCart.PlaceOrder("customer#home.com", "2 Widgets");
System.Console.Write("Press any key to end.");
System.Console.ReadLine();
}
}
}
Let's say MyHelper is used by MyService which in turn is used by your controller.
The way to resolve this situation is:
Register both MyService and MyHelper in Startup.ConfigureServices.
services.AddTransient<MyService>();
services.AddTransient<MyHelper>();
The controller receives an instance of MyService in its constructor.
public HomeController(MyService service) { ... }
MyService constructor will in turn receive an instance of MyHelper.
public MyService(MyHelper helper) { ... }
The DI framework will be able resolve the whole object graph without problems. If you are worried about new instances being created every time an object is resolved, you can read about the different lifetime and registration options like the singleton or request lifetimes.
You should be really suspicious when you think you have to manually create an instance of some service, as you might end up in the service locator anti-pattern. Better leave creating the objects to the DI Container. If you really find yourself in that situation (let's say you create an abstract factory), then you could use the IServiceProvider directly (Either request an IServiceProvider in your constructor or use the one exposed in the httpContext).
var foo = serviceProvider.GetRequiredService<MyHelper>();
I would recommend reading the specific documentation about the ASP.Net 5 DI framework and about dependency injection in general.
Unfortunately there is no direct way. The only way I managed to make it work is by creating a static class and using that everywhere else as below:
public static class SiteUtils
{
public static string AppName { get; set; }
public static string strConnection { get; set; }
}
Then in your startup class, fill it in as below:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//normal as detauls , removed for space
// set my variables all over the site
SiteUtils.strConnection = Configuration.GetConnectionString("DefaultConnection");
SiteUtils.AppName = Configuration.GetValue<string>("AppName");
}
Although this is bad pattern, as this will stay for the whole life cycle of the application and I couldn't find better way to use it outside controller.
Here's a more complete example to directly answer the OP's question, based on the current .NET Core 2.2 DI documentation here. Adding this answer since it may help someone that's new to .NET Core DI, and because this question is Google's top search result.
First, add an interface for MyHelper:
public interface IMyHelper
{
bool CheckIt();
}
Second, update the MyHelper class to implement the interface (in Visual Studio, press ctrl-. to implement the interface):
public class MyHelper : IMyHelper
{
private readonly ProfileOptions _options;
public MyHelper(IOptions<ProfileOptions> options)
{
_options = options.Value;
{
public bool CheckIt()
{
return _options.SomeBoolValue;
}
}
Third, register the interface as a framework-provided service in the DI service container. Do this by registering the IMyHelper service with the concrete type MyHelper in the ConfigureServices method in Startup.cs.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IMyHelper, MyHelper>();
...
}
Fourth, create a private variable to reference an instance of the service. Pass the service as an argument in the constructor (via constructor injection) then initialize the variable with the service instance. Reference any properties or call methods on this instance of the custom class via the private variable.
public class MessageCenterController : Controller
{
private readonly MyOptions _options;
private readonly IMyHelper _myHelper;
public MessageCenterController(
IOptions<MyOptions> options,
IMyHelper myHelper
)
{
_options = options.value;
_myHelper = myHelper;
}
public void DoSomething()
{
if (_myHelper.CheckIt())
{
// Do Something
}
}
}
You may use Activator.CreateInstance(). Here is a wrapper function for it. The way you use this is as follows.
var determinedProgrammatically = "My.NameSpace.DemoClass1"; // implements IDemo interface
var obj = CreateInstance<My.NameSpace.IDemo, string>(determinedProgrammatically, "This goes into the parameter of the constructor.", "Omit this parameter if your class lives in the current assembly");
Now you have an instance of obj which is instantiated from type determined programmatically. This obj can be injected into non controller classes.
public TInterface CreateInstance<TInterface, TParameter>(string typeName, TParameter constructorParam, string dllName = null)
{
var type = dllName == null ? System.Type.GetType(typeName) :
System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName.StartsWith(dllName, System.StringComparison.OrdinalIgnoreCase)).GetType(typeName);
return (TInterface)System.Activator.CreateInstance(type, constructorParam);
}
PS: You may iterate through System.AppDomain.CurrentDomain.GetAssemblies() to determine the name of the assembly that houses your class. This name is used in the 3rd parameter of the wrapper function.
TL;DR: You can save a singleton in a static var and then access it form other classes, but this an anti-pattern, use with caution.
Long version:
As per this question Resolving instances with ASP.NET Core DI from within ConfigureServices
Any services registered in ConfigureServices() can then be injected
into the Configure() method
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<FooService>();
}
public void Configure(IApplicationBuilder app, FooService fooService)
{
FooServiceInstance = fooService;
}
public static FooService FooServiceInstance { get; private set; }
And then call it from your other code MyStartupClass.FooService.DoStuff()
I have an ASP.NET 5 MVC 6 application. It has a Data Access library which needs a connection string to make a connection to the database.
Currently I am passing a strongly typed configuration settings class with connection string as a public property all the way up from the MVC controllers (Where it is received through DI) to the Data Access Class library.
I want to know if there is a better way for a class library to access strongly typed configuration settings using dependency injection or any other mechanism ?
Thank you.
EDIT : Code Example
This is a generic DbTransaction class which is called from the business layer.
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(string connectionString)
{
_query = new Query(connectionString);
}
public TEntity GetById(long id)
{
var sqlGenerator = new SqlGenerator<TEntity>();
var sql = sqlGenerator.GetSelectByIdQuery();
var mapper = new NMapper.Mapper<TEntity>();
var cmd = _query.GetNpgsqlCommand(sql, new { id });
return mapper.GetObject(cmd);
}
}
The query class creates the connection object from the connection string that is provided to it.
I agree with #Steven that using IOptions<T> is a bad idea. You can however use the ConfigurationBinder extensions to read out a specific section of configuration into a strongly-typed POCO class. Just make sure you have this somewhere in your project.json's dependencies section:
"dependencies": {
[other dependencies],
"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final",
[other dependencies]
}
Just build up your configuration as normal. For example, say you had a Database.json configuration file that looked like this:
{
"Database": {
"ConnectionInfo": {
"connectionString": "myConnectionString"
}
}
}
You can build your configuration from the Startup method in Startup.cs:
public IConfiguration Configuration { get; private set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) {
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("Database.json")
.AddEnvironmentVariables()
Configuration = configBuilder.Build();
}
Now we can make a POCO class to match the "Database:ConnectionInfo" section of the JSON configuraiton file. You can match it to an interface as #janhartmann suggests, but it may or may not be necessary.
public class DatabaseConnectionInfo {
public string ConnectionString { get; set; }
}
Now, how can we get that DatabaseConnectionInfo class populated with the data from the JSON config file? One way is to use the IOptions<T> framework type, but I don't like using framework types when I can avoid them. Instead, you can get an instance like so:
DatabaseConnectionInfo dbConnInfo = Configuration
.GetSection("Database:ConnectionInfo")
.Get<DatabaseConnectionInfo>();
Now you can just register the dbConnInfo type as a singleton of the type DatabaseConnectionInfo (or as a singleton of an interface type if you prefer to have an immutable configuration settings object). Once it's registered in the IoC container, you can constructor inject it where needed:
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(DatabaseConnectionInfo dbConnInfo)
{
_query = new Query(dbConnInfo.ConnectionString);
}
public TEntity GetById(long id) { ... }
}
You can let your service class depend on a an interface, e.g.:
public interface IConnectionFactory {
string ConnectionString();
}
public class MyDataAccessClass {
private readonly IConnectionFactory _connectionFactory
public MyDataAccessClass(IConnectionFactory connectionFactory) {
_connectionFactory = connectionFactory;
}
public void Whatever() {
var connectionString = _connectionFactory.ConnectionString();
}
}
And then make an implementation of it (as near to your composition root as possible):
public class SqlConnectionFactory : IConnectionFactory {
public string ConnectionString() {
return "myConnectionString";
}
}
Let the interface have the methods or properties you need.
Wire like:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConnectionFactory, SqlConnectionFactory>();
}
I use a similar method to some of those listed earlier, but I think its sufficiently different to warrant another answer.
Firstly I define an interface with all the configuration that my class needs. In this case
public interface IDbTransactionsConfiguration {
string ConnectionString { get; }
}
Then I alter my class to take this configuration via constructor injection
public class DbTransactions<TEntity> where TEntity : DbEntity, new() {
public DbTransactions(IDbTransactionsConfiguration configuration) {
...
}
}
Then I define a class that handles all the configuration for my application.
public class MyApplicationConfiguration : IDbTransactionsConfiguration, ISomeOtherConfiguration, etc {
public string ConnectionString { get; }
... other configuration
}
Then I pass this class into all classes that need it using some kind of Depenendency Injection (normally Castle Windsor or AutoFac for me).
If it is too difficult to construct DbTransactions for legacy type reasons, I define a static version of MyApplicationConfiguration and access this directly.
More details on this blog post.
I have a base Document DB repository in the infrastructure layer of my solution. I based this repository on this GitHub project, which is a static class that is utilized by my other domain model repositories.
In my API layer I have config.json files that are environment specific. I would like to use dependency injection to be able to use my configuration class that reads the DocumentDB settings defined in the API layer in the deeper Infrastructure layer. This StackOverflow answer details how to use DI with an API controller, however I can't figure out how to use DI in this case, as a static class, I don't have a constructor. Is it possible to use DI with my static repository? If not, how should I read config settings into the infrastructure layer?
My ConfigurationOptions class (in Infrastructure layer):
public class ConfigurationOptions
{
public string EndpointUri { get; set; }
}
My DocumentDbRepository class (in Infrastructure layer):
public static class DocumentDbRepository<T>
{
// I want to read these strings from my config.json files
private static string EndpointUri { get; } = ConfigurationOptions.EndpointUri;
//...
private static Document GetDocument(string id)
{
return Client.CreateDocumentQuery(Collection.DocumentsLink)
.Where(d => d.Id == id)
.AsEnumerable()
.FirstOrDefault();
}
}
Part of my Startup class (in my API layer)
public class Startup
{
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ConfigurationOptions>(options =>
options.EndpointUri = Configuration.Get("EndpointUri"));
// ...
}
// ...
}
I believe you are almost there.
The first step you have to take is almost done.
in your startup.cs, you have
services.Configure<ConfigurationOptions>(options =>
options.EndpointUri = Configuration.Get("EndpointUri"));
you can just call
services.Configure<ConfigurationOptions>(Configuration);
the services will map the EndpointUri attribute of your class. With this step 1 is done.
Now, following the post you linked, you can send your configurationOptions to the controller like:
public class SomeController
{
private string _endpointUrl;
public SomeController(IOptions<ConfigurationOptions> options)
{
_endpointUrl = options.Options.EndpointUri;
}
}
but, from what i assume, you want to have the EndpointUri in the DocumentDbRepository. You can do that in 2 ways:
1 --------------------------------------------------
You can create a constructor in your DocumentDbRepository to receive the EndpointUri and call it in your controller like such:
public class SomeController
{
private DocumentDbRepository _documentDbRepositoy
public SomeController(IOptions<ConfigurationOptions> options)
{
_documentDbRepositoy = new DocumentDbRepository (options.Options.EndpointUri);
}
}
2 ---------------------------------------------------
You can inject the DocumentDbRepository to all your controllers. For that i suggest that you create an interface IDocumentDbRepository and then configure it at startup making it a singleton or scoped or trasiend (for more info, see this link)
To do so, lets start with your DocumentDbRepository
public static class DocumentDbRepository<T> : IDocumentDbRepository<T>
{
private string EndpointUri { get; private set;}
public DocumentDbRepository(IOptions<ConfigurationOptions> options){
EndpointUri = options.Options.EndpointUri;
}
//...
}
then, at startup.cs you set it as singleton (in this example)
public void ConfigureServices(IServiceCollection services){
services.Configure<ConfigurationOptions(Configuration);
services.AddSingleton<IDocumentDbRepository, DocumentDbRepository>();
// ...
}
by doing this, if your controllers have a dependency on a IDocumentDbRepository, the singleton will be provided:
public class SomeController
{
private DocumentDbRepository _documentDbRepositoy
public SomeController(IDocumentDbRepository documentDbRepository)
{
_documentDbRepositoy = documentDbRepository
}
}