Question about Options pattern and dependency injection - c#

I have the following code in the Startup code of one project:
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddAppCommonServices(configuration)
.AddSingleton<IKeyVaultClient, KeyVaultClient>();
IKeyVaultClient keyVaultClient = services.BuildServiceProvider().GetService<IKeyVaultClient>();
services.RegisterMonitoringTelemetry(keyVaultClient.GetSecretAsync(configuration[Consts.AppInsightsInstrumentationKey]).Result);
...
}
This is working fine. But since we recently updated the .NET Core version, we started getting a warning message due to the call to the BuildServiceProvider() method. I was looking for how to properly write the code above to avoid the problem described by the warning and I understand that the right way is to use the Options Pattern to inject the necessary values. However, while I understand the concept, I'm still confused on how to actually use it in my case.
I tried this:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddOptions<KeyVaultClientSettings>()
.Configure<IKeyVaultClient>((settings, kvc) =>
{
settings.Secret = kvc.GetSecretAsync(configuration[Consts.AppInsightsInstrumentationKey]).Result;
}
);
services
.AddAppCommonServices(configuration)
.AddSingleton<IKeyVaultClient, KeyVaultClient>();
services
.Configure<KeyVaultClientSettings>(settings =>
{
services.RegisterMonitoringTelemetry(settings.Secret);
});
...
}
But it doesn't work. From debugging, it seems that the RegisterMonitoringTelemetry method is never called, though I'm not sure why.
Any help on how to fix my code, I would be super thankful!

Try this way. I think the issue is because you didn't create a scope. However even this solution is not the best practice.
Calling async method in ConfigureServices with .Result is also kind of bad stuff.
using var scope = services.BuildServiceProvider().CreateScope();
var ketVaultClient = scope.ServiceProvider.GetRequiredService<IKeyVaultClient>();
services.RegisterMonitoringTelemetry(keyVaultClient.GetSecretAsync(configuration[Consts.AppInsightsInstrumentationKey]).Result);

Related

SimpleInjector Container.Verify() with HTTP context-scoped dependency

I have the following basic SI registration in an ASP.NET WebApi project.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSimpleInjector(container, options =>
{
options
.AddAspNetCore()
.AddControllerActivation();
});
services.AddHttpContextAccessor();
services.AddScoped<Work>(services =>
{
var traceId = services.GetRequiredService<IHttpContextAccessor>().HttpContext.TraceIdentifier;
// ...
});
}
public void Configure(IApplicationBuilder app)
{
app.ApplicationServices.UseSimpleInjector(container);
// ...
container.Verify();
}
private readonly Container container = new Container();
The Problem
Container.Verify() attempts to resolve a Work instance, whose factory delegate successfully resolves an IHttpContextAccessor but its HttpContext is null because there is no current HTTP call on startup. Therefore the code dies with a null-reference exception.
I don't think there is anything we can do except guard against null but that goes against my taste in this context:
why would I do that when I know for a fact that this factory delegate should only be called during an HTTP call?
what exactly do I do if my HTTP-scoped dependency is null? Sure, return a fake BUT how do I detect that it's null for good reason and not because my web infrastructure is dying somehow?
I can't see a good solution. What do you do in this case?
I've wrote extensively about this problem in the past. Your HttpContext and its TraceIdentifier are runtime data and runtime data should be kept out of the construction of your object graphs. See for instance this article.
What the correct solution in your particular case is, is hard to say, because it depends on the details of what you are trying to construct, but the article gives some pointers.

.NET Core data protection - where is it used?

I am using .NET Core 3.1. I have the following code snippet in Startup.cs inside ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddDataProtection()
.SetApplicationName("MyApplication")
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine("AppData", "Keys")));
// ...
}
As far as I understand, this is only needed if I want to protect some input with IDataProtectionProvider, like so:
public class MyClass
{
readonly IDataProtectionProvider _rootProvider;
public MyClass(IDataProtectionProvider rootProvider)
{
_rootProvider = rootProvider;
}
public void Test()
{
IDataProtector protector = provider.CreateProtector("Test123");
string protectedPayload = protector.Protect("Hello world");
Console.WriteLine($"Protect returned: {protectedPayload}");
}
}
However, we are not using this functionality aynwhere in our application. Is it safe to remove AddDataProtection from ConfigureServices? Does any part of .NET Core application (TempData, AntiForgery tokens, ...) use it behind the scenes (so that Visual Studio doesn't find string IDataProtectionProvider)?
According to this document, here's a sentence said:
It cannot directly be used to protect or unprotect data. Instead, the
consumer must get a reference to an IDataProtector by calling
IDataProtectionProvider.CreateProtector(purpose)
hence based on this saying, it seems that data protection doesn't work as _rootProvider didn't be called in some place. And your another misgiving is some default setting or effect may work in some other places, you may refer to this document to see the common usage of data protection api.
And in my opinion, it's really hard to say it have no influence in your project as if by any chance we ignore some thing, that may lead to something unexpected. So if your app runs well now, why not just leave it there.

Move IWebHost reference from program to controller .NET Core 2.1 MVC

I have this code that's working in my main program function and I want to move it to the controller.
public static void Main(string[] args)
{
var webHost = CreateWebHostBuilder(args).Build();
var audioPlayer = (INodeServices)webHost.Services.GetService(typeof(INodeServices));
var playBackResult = audioPlayer.InvokeExportAsync<string>("./play-audio","play","/Users/ryandines/Projects/SqlServerApp/dopeTrapBeat9.mp3").Result;
webHost.Run();
}
So I implemented IWebHost interface, what is webHost called in technical terms? I need to be able to access webHost from the controller. If I try to change Main(), it complains about the signature, so I can't add parameters or change the return type in this case. What is the most sane way to get this code over to the controller so I can use it in my views?
Edit: Right now this code runs at startup. Instead of my audioPlayer being instantiated here, I need it in my controller, but I don't know how I'd make a reference to webHost in order to get that done.
Edit 2: Thanks Kirk, read the documentation, that was what I wanted to do. I think I'm almost there. It compiles, but no sound so I'm almost there. I tried this
public IActionResult Index([FromServices] INodeServices nodeServices)
{
var playbackResult = nodeServices.InvokeExportAsync<string>("./play-audio", "play", "/Users/ryandines/Projects/SqlServerApp/dopeTrapBeat9.mp3").Result;
return View();
}
Edit 3 (thanks first contributor to post an answer):
This is from Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddNodeServices();
...
}
So background info, using this service that other people wrote here https://github.com/aspnet/JavaScriptServices to play sound.
Credit goes to Kirk Larkin for explaining what I had to do and the fact that I didn't need IWebHost to get a reference to my service. My final bug was related to the file path, had to call it by its explicit path instead of ./play-audio. Final code:
public IActionResult Index([FromServices] INodeServices nodeServices)
{
var playbackResult = nodeServices.InvokeExportAsync<string>("/Users/ryandines/Projects/SqlServerApp/play-audio", "play", "/Users/ryandines/Projects/SqlServerApp/dopeTrapBeat9.mp3").Result;
return View();
}
Thank you for explaining this to me. Wish there was a way to give you more credit because you solved this for me. Hopefully I don't get super down voted like every other time I answer my own question.

How should I use appsettings.json config key/values in my ConfigurationServices ASP.NET Core 2.0 on startup?

I'm trying to configure my services for an ASP.NET Core 2.0 app/website.
I wish to reference some key/values from my appsettings.json file, in this method.
I'm not sure if what I'm going is OK or not:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters()
.AddCors();
var applicationSettings = Configuration.GetSection("Settings").Get<ApplicationSettings>();
services.AddSingleton(applicationSettings);
// ** THIS IS WHAT I ORIGINALLY HAD, BUT IT'S ONLY SETTING
// THE VALUE IN DI/IOC.
//services.Configure<ApplicationSettings>(options => Configuration.GetSection("Settings")
// .Bind(options));
var foo = new Foo(applicationSettings.SomeSetting);
services.AddSingleton(foo);
}
See how i'm manually adding a singleton and then later, referring a value from the app settings instance?
vs
just configuring ...
So, is either way OK or is there a specific reason for either/or?
Remember -> i'm going to need to inject my settings into controllers, etc...
Technically, you can do either. In both cases, you have the configuration registered and available through dependency injection, so everything can depend on it and will get the configuration instance.
You are also using the centrally set up Configuration there, so you have all the benefits from the configuration stack there, e.g. multiple providers or environment specific overrides.
However, the favor has definitely moved to the IOptions way of consuming custom configuration. It’s the “state of the art” and used throughout ASP.NET Core for literally everything. It also allows you to switch to options that can be updated at runtime. That’s very powerful and might become useful eventually (not necessarily for your specific situation with the singleton, but maybe for something else).
It’s also really easy to set this up, actually shorter than what you tried:
services.Configure<ApplicationSettings>(Configuration.GetSection("Settings"));
services.AddSingleton<Foo>();
Note that, even for singletons, you shouldn’t explicitly create a new instance of it, but let DI handle that. If your class has the correct constructor, dependencies will be automatically injected anyway:
public class Foo
{
private readonly ApplicationSettings _settings;
public Foo (IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
}
Of course, Foo can also have more dependencies here. Since it’s going to be constructed by DI, you can just add more dependencies in the constructor, without having to update some new call somewhere.
If you need to configure certain services with settings that depend on your configuration, you still should not bind your configuration there directly. All of configuration is DI-based, so you just need to inject the right thing; a IConfigureOptions<T>. That’s basically the thing that provides the IOptions<T> to services later. In your JWT case, this could look like this:
// instead of passing an option configuration delegate here…
services.AddAuthentication().AddJwtBearer();
// … we register a IConfigureOptions<JwtBearerOptions> instead
services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerOptions>();
// … ConfigureJwtBearerOptions could look like this:
class ConfigureJwtBearerOptions : IConfigureOptions<JwtBearerOptions>
{
private readonly ApplicationSettings _settings;
public ConfigureJwtBearerOptions(IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
public void Configure(JwtBearerOptions options)
{
// configure JwtBearerOptions here, and use your ApplicationSettings
options.MetadataAddress = _settings.JwtMetadataAddress;
}
}
This might seem unnecessarily verbose compared to just passing a delegate to AddJwtBearer() but note that this is exactly what happens under the hood when you pass that delegate: An IConfigureOptions<JwtBearerOptions> object will be created that calls your delegate in the Configure() call. So this is really just the same.
Note that for authentication schemes, you might actually set up a IConfigureNamedOptions<T> instead, which is almost the same thing except it can configure the options based on a name. For authentication schemes, that is the scheme name, so basically you check the scheme name in Configure() and then decide how to configure your options.
As for creating singleton instances, especially expensive ones, I would argue that ConfigureServices is the wrong place for such a thing. ConfigureServices is called very early in the application startup phase, when the whole DI infrastructure does not exist yet. So you could not rely on anything when creating your instance. I would also argue that it is still not your job to create the object but you should DI handle the creation of it and as such give it also control over its lifecycle.
If you absolutely need to control when the instance is created, I would suggest you to use the lifecycle events for this: Basically, after the application has set up properly but before a first request comes in, you request the instance of your services and initialize it. That way, you can still have it fully depend on DI, and it won’t be created lazily with the first request.
You can register lifecycle handlers in the Configure method:
public void Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStarted.Register(() =>
{
// application has started, request the singleton here to trigger DI to
// create the instance
app.ApplicationServices.GetService<ExpensiveSingleton>();
});
// …
}
}
Well the problem with that approach is that it will be impossible to load multiple configuration sections through DI. The Configuration API has many features, such as pluggable configuration provides, snapshots, etc.
I would suggest you at least use a class to bind you configuration section against, so DI can inject it based on its type. If you further down the line have need to another configuration class you won't run into issues.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration

Application startup code in ASP.NET Core

Reading over the documentation for ASP.NET Core, there are two methods singled out for Startup: Configure and ConfigureServices.
Neither of these seemed like a good place to put custom code that I would like to run at startup. Perhaps I want to add a custom field to my DB if it doesn't exist, check for a specific file, seed some data into my database, etc. Code that I want to run once, just at app start.
Is there a preferred/recommended approach for going about doing this?
I agree with the OP.
My scenario is that I want to register a microservice with a service registry but have no way of knowing what the endpoint is until the microservice is running.
I feel that both the Configure and ConfigureServices methods are not ideal because neither were designed to carry out this kind of processing.
Another scenario would be wanting to warm up the caches, which again is something we might want to do.
There are several alternatives to the accepted answer:
Create another application which carries out the updates outside of your website, such as a deployment tool, which applies the database updates programmatically before starting the website
In your Startup class, use a static constructor to ensure the website is ready to be started
Update
The best thing to do in my opinion is to use the IApplicationLifetime interface like so:
public class Startup
{
public void Configure(IApplicationLifetime lifetime)
{
lifetime.ApplicationStarted.Register(OnApplicationStarted);
}
public void OnApplicationStarted()
{
// Carry out your initialisation.
}
}
This can be done by creating an IHostedService implementation and registering it using IServiceCollection.AddHostedService<>() in ConfigureServices() in your startup class.
Example
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
public class MyInitializer : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
// Do your startup work here
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
// We have to implement this method too, because it is in the interface
return Task.CompletedTask;
}
}
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<MyInitializer>();
}
}
Notes
The main application will not be started until after your code has finished executing.
Constructor dependency injection is available to the IHostedService implementation.
I can recommend this blog post for more info, and an example of how to use async: https://andrewlock.net/running-async-tasks-on-app-startup-in-asp-net-core-3/
For more background reading, see this discussion: https://github.com/dotnet/aspnetcore/issues/10137
Basically there are two entry points for such custom code at startup time.
1.) Main method
As a ASP.NET Core application has got the good old Main method as entry point you could place code before the ASP.NET Core startup stuff, like
public class Program
{
public static void Main(string[] args)
{
// call custom startup logic here
AppInitializer.Startup();
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
2.) Use your Startup class
As you already stated in your question is the Configure and ConfigureServices a good place for your custom code.
I would prefer the Startup class. From the runtime perspective it does not matter, if the call is called in startup or somewhere else before the host.Run() call. But from a programmer's point of view who is accustomed to the ASP.NET framework then his first look for such logic would be the Startup.cs file. All samples and templates put there the logic for Identity, Entity Framework initialization and so on. So as a convention I recommend to place the initialization stuff there.

Categories