- A change token is a general-purpose, low-level building block used to track changes.
- Change tokens are used in prominent areas of ASP.NET Core monitoring changes to objects:
- For monitoring changes to files,
IFileProvider
's Watch
method creates an IChangeToken
for the specified files or folder to watch.
IChangeToken
tokens can be added to cache entries to trigger cache evictions on change.
- For
TOptions
changes, the default OptionsMonitor implementation of IOptionsMonitor has an overload that accepts one or more IOptionsChangeTokenSource
instances.
- Each instance returns an
IChangeToken
to register a change notification callback for tracking options changes.
IChangeToken
IChangeToken
propagates notifications that a change has occurred. It has two properties:
ActiveChangedCallbacks
indicate if the token proactively raises callbacks. If ActiveChangedCallbacks
is set to false
, a callback is never called, and the app must poll HasChanged
for changes. It's also possible for a token to never be cancelled if no changes occur or the underlying change listener is disposed or disabled.
HasChanged
gets a value that indicates if a change has occurred.
- The interface has one method,
RegisterChangeCallback(Action<Object>, Object)
, which registers a callback that's invoked when the token has changed.
HasChanged
must be set before the callback is invoked.
ChangeToken
Class
ChangeToken
is a static class used to propagate notifications that a change has occurred.
- The
ChangeToken
OnChange(Func<IChangeToken>, Action)
method registers an Action
to call whenever the token changes:
Func<IChangeToken>
produces the token.
Action
is called when the token changes.
ChangeToken
has an OnChange<TState>(Func<IChangeToken>, Action<TState>, TState)
overload that takes an additional TState
parameter that's passed into the token consumer Action
.
OnChange
returns an IDisposable
.
- Calling
Dispose
stops the token from listening for further changes and releases the token's resources.
Monitoring for Configuration Changes
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
- File-based configuration is represented by
FileConfigurationSource
.
- The sample app demonstrates two implementations for monitoring configuration changes.
- If either the appsettings.json file changes or the Environment version of the file changes, each implementation executes custom code.
- A configuration file's
FileSystemWatcher
can trigger multiple token callbacks for a single configuration file change.
- The sample's implementation guards against this problem by checking file hashes on the configuration files.
- Checking file hashes ensures that at least one of the configuration files has changed before running the custom code.
public static byte[] ComputeHash(string filePath)
{
var runCount = 1;
while(runCount < 4)
{
try
{
if (File.Exists(filePath))
{
using (var fs = File.OpenRead(filePath))
{
return System.Security.Cryptography.SHA1.Create().ComputeHash(fs);
}
}
}
catch (IOException ex)
{
if (runCount == 3 || ex.HResult != -2147024864)
{
throw;
}
else
{
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
}
return new byte[20];
}
- A retry is implemented with an exponential back-off.
- The re-try is present because file locking may occur that temporarily prevents computing a new hash on one of the files.
Simple Startup Change Token