Struggling a little with Dependency Injection/Scoped Services with a rewrite rule class.
I have a redirects class which implements IRule
class ActivateRedirects : IRule
{
public void ApplyRule(RewriteContext context)
{
// Do stuff here which relies on CoreSettings (info below)
}
}
I also have a CoreSettings
class which contains various settings, some of which are required for ApplyRule
to work, it is initialised in startup.cs
as follows.
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "site.json"), optional: false, reloadOnChange: true);
IConfigurationRoot root = configurationBuilder.Build();
CoreSettings s = new CoreSettings();
services.Configure<CoreSettings>(root.GetSection("Website"));
So you can see above, CoreSettings
is created as a Service, which in most cases I can consume with DI:
public class SomeOtherClass{
private CoreSettings Settings;
public SomeOtherClass(Microsoft.Extensions.Options.IOptionsSnapshot<CoreSettings> S)
{
Settings = S.Value;
}
// Do stuff with Settings ....
}
I have read up on several pages on why I can't simply add DI to the ActivateRedirects
Class, or explicitly pass the value in using app.ApplicationServices.GetRequiredService
but everything I have read is telling what I can't do, I can't find anything to tell me what I can!!
Before I am told to rewrite the code to not require CoreSettings
for Rewriting, I can't do that because one of the rewrite rules depends on a condition which is set by a remote server via a REST API
and what is needed in the CoreSettings
class is the API credentials used to create the REST Client
.
It is possible to do what you want. I'll assume your class is defined as follows:
public class ActivateRedirects : IRule
{
private readonly CoreSettings _coreSettings;
public ActivateRedirects(CoreSettings coreSettings)
{
_coreSettings = coreSettings;
}
public void ApplyRule(RewriteContext context)
{
}
}
Read the configuration file, as before:
services.Configure<CoreSettings>(root.GetSection("Website"));
Next, setup your RewriteOptions
:
var coreSettings = app.ApplicationServices.GetRequiredService<IOptions<CoreSettings>>();
var activateRedirects = new ActivateRedirects(coreSettings.Value);
var rewriteOptions = new RewriteOptions();
rewriteOptions.Rules.Add(activateRedirects);
app.UseRewriter(rewriteOptions);
If you put a breakpoint inside ActivateRedirects
, and send a request, you'll see the CoreSettings
field has been populated.
I think this scenario is what IOptionsMonitor<T>
might be designed for. It's registered as a singleton, but is notified of options changes. Change ActivateRedirects
to:
public class ActivateRedirects : IRule
{
private readonly IOptionsMonitor<CoreSettings> _coreSettings;
public ActivateRedirects(IOptionsMonitor<CoreSettings> coreSettings)
{
_coreSettings = coreSettings;
}
public void ApplyRule(RewriteContext context)
{
}
}
and change how the instance is constructed to:
var coreSettings = app.ApplicationServices.GetRequiredService<IOptionsMonitor<CoreSettings>>();
var activateRedirects = new ActivateRedirects(coreSettings);
For me, editing the configuration does now show updated values in CoreSettings
. One caveat is I don't know how that notification process works. If this ends up reading the configuration directly on each request, then it will scale really poorly, so I'd advise giving it a good test first.