Using IConfigureOptions to configure an injected dependency - c#

I have created an ASP.NET Core 1.0.1 WebApi project and am trying to initialize an injected dependency with some custom options before using it in my controllers. After searching online I found a few articles (here, here and here explaining how to use IConfigureServices to do just this. Seems pretty simple! Unfortunately, I can't get it to work and I can't figure out why, I'm sure it must be a simple oversight..
I have created a simple project, and added the following classes to illustrate the most basic scenario:
public class Tester
{
public void Initialize(TestOptions options)
{
//do something with options.
}
}
public class TestConfigurator : IConfigureOptions<TestOptions>
{
private Tester _tester;
public TestConfigurator(Tester tester)
{
_tester = tester;
}
public void Configure(TestOptions options)
{
_tester.Initialize(options);
}
}
public class TestOptions
{
}
The 'Tester' class gets injected into the constructor of a Controller class:
[Route("api/[controller]")]
public class ValuesController : Controller
{
public ValuesController(Tester tester)
{
//do something with tester..
}
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Finally, I have added the following configuration in ConfigureServices of the Startup class:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddOptions();
services.AddMvc();
services.AddSingleton<Tester, Tester>();
services.AddSingleton<IConfigureOptions<TestOptions>, TestConfigurator>();
}
When I run the project and call the 'api/values' call, the Tester class is created and injected into the ValuesController, but the TestConfigurator class never gets constructed and so the class never gets Initialized with the options class. What am I missing?
UPDATE
The answers below are of course all valid to this simplified example. I realize now that I oversimplified a bit, as the dependency I'm using (illustrated here as Tester) is from a 3rd party library, so I don't have the constructor to play with. Wrapping the 3rd party class in an extended class will do the trick, but if anybody has an alternative suggestion on manipulating it without modifying its constructor, then I'm still open to suggestions, thanks.

Ok, now I got it, I feel silly for all the edits.
you are using IOptions wrong, and it got me all confused.
implementing a custom IConfigurationOptions<> gives you the abilty to either configure your options from database, or to just use a different class (instead of a lambda)
what you are trying to do, is instantiate a Tester class based on those options, this is fine - but it's not the IConfigureOptions<> job.
in order to initialize your Tester class based on the TestOptions you should create a constructor on the Tester class that receives it like this
public class Tester
{
public Tester(IOptions<TestOptions> options)
{
//do something with options.
}
}
and what you are trying to do will work.

Taken from Asp.Net Core Configuration Documentation and adapted to your example
Assuming
public class TestOptions {
public string SomeOption { get; set; }
}
Options can be injected into your application using the
IOptions<TOptions> accessor service.
You could try abstracting Tester and registering that with the service collection.
public interface ITester {
//tester contract
}
public class Tester : ITester {
public Tester(IOptions<TestOptions> options) {
//do something with test options.
}
}
To setup the IOptions<TOptions> service you call the AddOptions
extension method during startup in your ConfigureServices method.
You configure options using the Configure<TOptions> extension method.
You can configure options using a delegate or by binding your options
to configuration:
public void ConfigureServices(IServiceCollection services) {
services.AddApplicationInsightsTelemetry(Configuration);
// Setup options with DI
services.AddOptions();
// Configure TestOptions using config by installing Microsoft.Extensions.Options.ConfigurationExtensions
services.Configure<TestOptions>(Configuration);
// Configure TestOptions using code
services.Configure<TestOptions>(testOptions => {
testOptions.SomeOption = "value1_from_action";
});
// Add framework services.
services.AddMvc();
// Add your services.
services.AddSingleton<ITester, Tester>();
}
And finally just refactor the controller to depend on the abstraction instead of the concretion.
[Route("api/[controller]")]
public class ValuesController : Controller {
public ValuesController(ITester tester) {
//do something with tester..
}
// GET api/values
[HttpGet]
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
}

Where is TestOptions supposed to come from? Are you trying to get it mapped auto-magically from your settings file? I think you are over-thinking how this should work, unless there is some reason you have to use initialize instead of constructor injection.
All you are trying to do is make some options available to tester, right?
If so, just use the basic IOptions features - no need to go advanced with IConfigureOptions
public class Tester
{
private TestOptions _options;
public Tester(IOptions<TestOptions> options)
{
_options = options.Value;
}
}
// don't need this anymore
/* public class TestConfigurator : IConfigureOptions<TestOptions>
{
private Tester _tester;
public TestConfigurator(Tester tester)
{
_tester = tester;
}
public void Configure(TestOptions options)
{
_tester.Initialize(options);
}
}
*/
public class TestOptions
{
}
And then configure the options using one of the two methods below (depending on whether it comes from config or has to be manually constructed).
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddOptions();
services.AddMvc();
services.AddSingleton<Tester, Tester>();
// Configure TestOptions using config
services.Configure<TestOptions>(Configuration);
// Configure MyOptions using code
services.Configure<TestOptions>(testOptions =>
{
// initialize them here, e.g. testOptions.Foo = "Bar"
});
}

Related

StructureMap does not see types in ASP.NET Core .NET5

I am creating sample project based on DDD.
I created SharedKernel project where I have my class for DomainEvents
public static class DomainEvents
{
public static IContainer Container { get; set; }
static DomainEvents()
{
Container = StructureMap.Container.For<GenericTypesScanning>();
}
public static void Raise<T>(T args) where T : IDomainEvent
{
foreach (var handler in Container.GetAllInstances<IHandle<T>>())
{
handler.Handle(args);
}
}
}
and this is class GenericTypesScanning
public class GenericTypesScanning : Registry
{
public GenericTypesScanning()
{
Scan(scan =>
{
// 1. Declare which assemblies to scan
scan.Assembly("MyLib");
// 2. Built in registration conventions
scan.AddAllTypesOf(typeof(IHandle<>));
scan.WithDefaultConventions();
});
}
}
In MyLib project I have class AppointmentConfirmedEvent and handler for this event:
public class EmailConfirmationHandler: IHandle<AppointmentConfirmedEvent>
{
public void Handle(AppointmentConfirmedEvent appointmentConfirmedEvent)
{
// TBD
}
}
I have temporary rest api controller where I wanted to check if everything is correctly registered and I am doing this:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET: api/<ValuesController>
[HttpGet]
public IEnumerable<string> Get()
{
var appointmentConfirmedEvent = new AppointmentConfirmedEvent();
DomainEvents.Raise(appointmentConfirmedEvent);
return new string[] { "value1", "value2" };
}
}
but when DomainEvents.Raise is called the event is not handled because internal call Container.GetAllInstances<IHandle<T>>() returns empty array.
I did analogous example with Console app and there everything works fine. Any idea why it does not work in case of ASP.NET Core .NET 5 project?
-Jacek
The AddAllTypesOf() method does not work with open generics. See the ConnectImplementationsToTypesClosing() method in the StructureMap docs: http://structuremap.github.io/generics/
And just a reminder, StructureMap is no longer supported. Moreover, 2.6.4.1 was the "haunted" version of StructureMap that was admittedly buggy.
The first thing to do is to check out the type scanning diagnostics:
http://structuremap.github.io/diagnostics/type-scanning/.
The type scanning can be a little brittle if there are missing assemblies. The diagnostics might point out where things are going wrong. Also, try your WhatDoIHave() diagnostics too.
And also, just making sure that you know that StructureMap is no longer supported and has been replaced by Lamar:
https://jeremydmiller.com/2018/01/29/sunsetting-structuremap/
https://jasperfx.github.io/lamar

How to Dependency inject Data Access Layer which takes database settings in it's constructor?

I wrote a simple Data access interface with generic CRUD operations so that every database repository has to implement this interface. For now I implemented this interface for one database say SQL. This SQL implementation of the database takes database settings(connection string and other details) as parameter in it's constructor.
See below code:
namespace MyDataAccessInterfaces
{
public interface IDataAccess
{
read();
create();
update();
delete();
}
}
namespace MyDataAccessImplementations
{
public class SQLAccess
{
SQLAccess(Settings settings){
//do something to get DB context
}
//some properties here
read(){//some code
}
create(){//some code
}
update(){//some code
}
delete(){//some code
}
}
}
Now I want to dependency inject this into a Service controller. I'm looking at constructor dependency injection here. If I pass SQL implementation as dependency in controller constructor as below and add this in startup class->ConfigureServie method, how to pass it's settings?
public class HomeController : APIController
{
private readonly IDataAccess _dataAccess;
public HomeController(IDataAccess dataAccess)
{
_dataAccess= dataAccess;
}
public IActionResult Index()
{
//do something and set result
return Ok(result);
}
}
Below is my ConfigureService method in startup.cs class
public void ConfigureServices(IServiceCollection services)
{
// Add application services.
services.AddSingleton<IDataAccess, SQLAccess>();
}
Question is, How do I pass the constructor parameters and property values in API's Configure Service method?
public void ConfigureServices(IServiceCollection services)
{
// Add application services.
services.AddSingleton<IDataAccess, SQLAccess>(opt=> { new SQLAccess(new
Settings(){
//Set Settings object properties here
});
});
}

Dependency Injection with classes other than a Controller class

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()

.NET Core - Trying to add a repository to my API controller, but when I do every controller method returns a 500 error

As the title says. I'm creating a Web API and in my API controller, I'm trying to declare a repository in the constructor. I successfully declare it, but every API method I try to call in that controller returns a 500 error. When I remove the constructor/repository variable, I have no issues.
Controller
[Route("api/[controller]")]
public class TestController: Controller
{
private ITestRepository _testRepository;
public TestController(ITestRepository testRepository)
{
_testRepository= testRepository;
}
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services
.AddMvcCore()
.AddJsonFormatters()
.AddApiExplorer();
services.AddScoped<ITestRepository , TestRepository >();
services.AddSwaggerGen();
}
Am I missing something?
Short Answer
I'm trying to declare a repository in the constructor. I successfully declare it, but every API method I try to call in that controller returns a 500 error. When I remove the constructor/repository variable, I have no issues.
You probably need to make one of two changes:
remove the parameters from the repository's constructor, or
register the services that the repository's constructor takes.
Explanation
The exact code from your question works with the following repository code.
public interface ITestRepository { }
public class TestRepository : ITestRepository { }
The code throws a 500 error, though, if the constructor takes a parameter.
public class TestRepository : ITestRepository
{
public TestRepository(object someObject)
{
}
}
It throws with that constructor, because a call to services.AddScoped<ITestRepository, TestRepository>() requires that the TestRepository constructor meets one of these two criteria.
a constructor without parameters, or
a constructor that takes resolvable services.
So to fix your code you need to make one of two changes:
remove the parameters from the constructor, or
register the services that your constructor takes.
For instance, if the repository takes a DbContext in its constructor, then your code might look like this.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters()
.AddApiExplorer();
services
.AddEntityFramework()
.AddInMemoryDatabase()
.AddDbContext<TestDbContext>(); // register a service
services.AddScoped<ITestRepository, TestRepository>();
services.AddSwaggerGen();
}
TestRepository.cs
public class TestRepository : ITestRepository
{
// pass the registered service to the ctor
public TestRepository(TestDbContext testDbContext)
{
}
}
First we register the dependent component using Microsoft.practices.Unity, and second we resolve them where we are to use them.
You have not resolved your dependency before using it.
public class TestController: Controller
{
private ITestRepository _testRepository;
public TestController(ITestRepository testRepository)
{
_testRepository= testRepository;
}
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Registering here:
DIContainer.Instance.RegisterType<ITagManager, TagManager>();
We resolve our dependencies before using them.
DIContainer.Instance.Resolve<ITagManager>().RetrieveTwitterTags();

Dependency Injection of configuration class into static Document DB repository (VS2015 DNX project)

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

Categories