Обнаружение изменений с помощью токенов изменений в ASP.NET Core
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 9 этой статьи.
Токен изменений — это низкоуровневый стандартный блок общего назначения, используемый для отслеживания изменений.
Просмотреть или скачать образец кода (описание загрузки)
Интерфейс IChangeToken
IChangeToken распространяет уведомления о том, что произошло изменение. IChangeToken
находится в пространстве имен Microsoft.Extensions.Primitives. Пакет Microsoft.Extensions.Primitives NuGet явно предоставляется приложениям ASP.NET Core.
IChangeToken
имеет два свойства:
- ActiveChangeCallbacks указывает, выполняет ли токен обратные вызовы с упреждением. Если для
ActiveChangedCallbacks
задано значениеfalse
, обратный вызов не выполняется, а приложению нужно опроситьHasChanged
на предмет изменений. Кроме того, отмена токена может никогда не произойти в случае отсутствия изменений или отключения либо удаления базового прослушивателя изменений. - HasChanged получает значение, указывающее, произошло ли изменение.
Интерфейс IChangeToken
включает метод RegisterChangeCallback(Action<Object>, Object), который регистрирует обратный вызов, выполняемый при изменении токена. Перед выполнением обратного вызова нужно задать HasChanged
.
Класс ChangeToken
ChangeToken — это статический класс, который распространяет уведомления о том, что произошло изменение. ChangeToken
находится в пространстве имен Microsoft.Extensions.Primitives. Пакет Microsoft.Extensions.Primitives NuGet явно предоставляется приложениям ASP.NET Core.
Метод ChangeToken.OnChange(Func<IChangeToken>, Action) регистрирует объект Action
, вызываемый при изменении токена:
Func<IChangeToken>
создает токен.Action
вызывается при изменении токена.
Перегрузка ChangeToken.OnChange<TState>>(Func<IChangeToken>, Action<TState>, TState) принимает дополнительный параметр TState
, передаваемый в объект-получатель токена Action
.
OnChange
возвращает IDisposable. При вызове Dispose токен прекращает прослушивать дальнейшие изменения и освобождает свои ресурсы.
Примеры использования токенов изменений в ASP.NET Core
Токены изменений используются в ключевых областях ASP.NET Core для отслеживания изменений в объектах:
- Чтобы отслеживать изменения в файлах, метод Watch интерфейса IFileProvider создает
IChangeToken
для указанных файлов или папки. - Токены
IChangeToken
можно добавлять в записи кэша, чтобы активировать вытеснения кэша при изменении. - Для изменений
TOptions
используемая по умолчанию реализация OptionsMonitor<TOptions> интерфейса IOptionsMonitor<TOptions> имеет перегрузку, которая принимает один или несколько экземпляров IOptionsChangeTokenSource<TOptions>. Каждый экземпляр возвращаетIChangeToken
, чтобы зарегистрировать обратный вызов уведомления об изменении для отслеживания изменений параметров.
Отслеживание изменений конфигурации
По умолчанию шаблоны ASP.NET Core используют файлы конфигурации JSON (appsettings.json
, appsettings.Development.json
и appsettings.Production.json
) для загрузки параметров конфигурации приложения.
Эти файлы настраиваются с помощью метода расширения AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean) в ConfigurationBuilder, который принимает параметр reloadOnChange
. reloadOnChange
указывает, нужно ли перезагружать конфигурацию при изменении файла. Этот параметр отображается в удобном методе CreateDefaultBuilder класса Host:
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true,
reloadOnChange: true);
Конфигурация на основе файла представлена FileConfigurationSource. FileConfigurationSource
использует IFileProvider для отслеживания файлов.
По умолчанию IFileMonitor
предоставляется объектом PhysicalFileProvider, который использует FileSystemWatcher для отслеживания изменений в файле конфигурации.
Этот пример приложения демонстрирует две реализации для отслеживания изменений конфигурации. Если любой из appsettings
файлов изменяется, обе реализации мониторинга файлов выполняют пользовательский код— пример приложения записывает сообщение в консоль.
FileSystemWatcher
файла конфигурации может активировать несколько обратных вызовов токена для одного изменения файла конфигурации. Чтобы пользовательский код выполнялся один раз при активации нескольких обратных вызовов токена, реализация в примере проверяет хэши файлов. В примере используется хэширование файлов SHA1. Повторная попытка реализуется посредством экспоненциальной задержки.
Utilities/Utilities.cs
:
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);
}
}
else
{
throw new FileNotFoundException();
}
}
catch (IOException ex)
{
if (runCount == 3)
{
throw;
}
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
return new byte[20];
}
Токен изменений для простого запуска
Зарегистрируйте обратный вызов Action
объекта-получателя токена для уведомлений об изменениях в токене перезагрузки конфигурации.
В Startup.Configure
:
ChangeToken.OnChange(
() => config.GetReloadToken(),
(state) => InvokeChanged(state),
env);
config.GetReloadToken()
предоставляет токен. Обратный вызов является методом InvokeChanged
:
private void InvokeChanged(IWebHostEnvironment env)
{
byte[] appsettingsHash = ComputeHash("appSettings.json");
byte[] appsettingsEnvHash =
ComputeHash($"appSettings.{env.EnvironmentName}.json");
if (!_appsettingsHash.SequenceEqual(appsettingsHash) ||
!_appsettingsEnvHash.SequenceEqual(appsettingsEnvHash))
{
_appsettingsHash = appsettingsHash;
_appsettingsEnvHash = appsettingsEnvHash;
WriteConsole("Configuration changed (Simple Startup Change Token)");
}
}
state
обратного вызова используется для передачи IWebHostEnvironment
. Это удобно для определения правильного файла конфигурации appsettings
, который требуется отслеживать (например, appsettings.Development.json
при работе в среде разработки). Хэши файлов используются для предотвращения многократного выполнения оператора WriteConsole
из-за нескольких обратных вызовов токена при всего одном изменении файла конфигурации.
Эта система выполняется, пока запущено приложение, и не может быть отключена пользователем.
Отслеживание изменений конфигурации как служба
Этот пример реализует следующее:
- Базовое отслеживание токена запуска.
- Отслеживание как служба.
- Механизм для включения и отключения отслеживания.
Этот пример задает интерфейс IConfigurationMonitor
.
Extensions/ConfigurationMonitor.cs
:
public interface IConfigurationMonitor
{
bool MonitoringEnabled { get; set; }
string CurrentState { get; set; }
}
Конструктор реализованного класса ConfigurationMonitor
регистрирует обратный вызов для уведомлений об изменениях:
public ConfigurationMonitor(IConfiguration config, IWebHostEnvironment env)
{
_env = env;
ChangeToken.OnChange<IConfigurationMonitor>(
() => config.GetReloadToken(),
InvokeChanged,
this);
}
public bool MonitoringEnabled { get; set; } = false;
public string CurrentState { get; set; } = "Not monitoring";
config.GetReloadToken()
предоставляет токен. InvokeChanged
является методом обратного вызова. state
в этом экземпляре является ссылкой на экземпляр IConfigurationMonitor
, используемый для доступа к состоянию мониторинга. Используются два свойства:
MonitoringEnabled
: указывает, должен ли обратный вызов запускать его пользовательский код.CurrentState
: описывает текущее состояние мониторинга для использования в пользовательском интерфейсе.
Метод InvokeChanged
похож на описанный ранее подход, за исключением того, что он:
- не выполняет свой код, если только
MonitoringEnabled
не имеет значениеtrue
; - выводит текущее значение
state
в своих выходных данныхWriteConsole
.
private void InvokeChanged(IConfigurationMonitor state)
{
if (MonitoringEnabled)
{
byte[] appsettingsHash = ComputeHash("appSettings.json");
byte[] appsettingsEnvHash =
ComputeHash($"appSettings.{_env.EnvironmentName}.json");
if (!_appsettingsHash.SequenceEqual(appsettingsHash) ||
!_appsettingsEnvHash.SequenceEqual(appsettingsEnvHash))
{
string message = $"State updated at {DateTime.Now}";
_appsettingsHash = appsettingsHash;
_appsettingsEnvHash = appsettingsEnvHash;
WriteConsole("Configuration changed (ConfigurationMonitor Class) " +
$"{message}, state:{state.CurrentState}");
}
}
}
Экземпляр ConfigurationMonitor
регистрируется как служба в Startup.ConfigureServices
:
services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();
Страница индекса позволяет пользователю управлять отслеживанием конфигурации. Экземпляр IConfigurationMonitor
внедряется в IndexModel
.
Pages/Index.cshtml.cs
:
public IndexModel(
IConfiguration config,
IConfigurationMonitor monitor,
FileService fileService)
{
_config = config;
_monitor = monitor;
_fileService = fileService;
}
Монитор конфигурации (_monitor
) позволяет включить или отключить мониторинг и задать текущее состояние для обратной связи пользовательского интерфейса:
public IActionResult OnPostStartMonitoring()
{
_monitor.MonitoringEnabled = true;
_monitor.CurrentState = "Monitoring!";
return RedirectToPage();
}
public IActionResult OnPostStopMonitoring()
{
_monitor.MonitoringEnabled = false;
_monitor.CurrentState = "Not monitoring";
return RedirectToPage();
}
При активации OnPostStartMonitoring
отслеживание включается, а текущее состояние сбрасывается. При активации OnPostStopMonitoring
отслеживание отключается, а состояние указывает на отсутствие отслеживания.
Кнопки пользовательского интерфейса для включения и отключения отслеживания.
Pages/Index.cshtml
:
<button class="btn btn-success" asp-page-handler="StartMonitoring">
Start Monitoring
</button>
<button class="btn btn-danger" asp-page-handler="StopMonitoring">
Stop Monitoring
</button>
Отслеживание изменений кэшированных файлов
Содержимое файла можно кэшировать в памяти с помощью IMemoryCache. Кэширование в памяти описано в разделе Кэш в памяти. Устаревшие (просроченные) данные возвращаются из кэша при изменении источника исходных данных без каких-либо дополнительных действий, таких как описанная ниже реализация.
Например, если не учитывать состояние кэшированного исходного файла при продлении скользящего срока действия, это приведет к появлению в кэше устаревших данных файлов. Каждый запрос к данным продляет скользящий срок действия, но этот файл никогда не загружается в кэш. Любые функции приложения, использующие кэшированное содержимое, могут получить устаревшее содержимое.
Использование токенов изменений в сценарии кэширования файлов предотвращает появление устаревшего содержимого файлов в кэше. Этот пример демонстрирует реализацию данного подхода.
GetFileContent
в примере используется для:
- возврата содержимого файла;
- Реализуйте алгоритм повторных попыток с экспоненциальным обратным отключением, чтобы охватить случаи, когда проблема доступа к файлам временно задерживает чтение содержимого файла.
Utilities/Utilities.cs
:
public async static Task<string> GetFileContent(string filePath)
{
var runCount = 1;
while(runCount < 4)
{
try
{
if (File.Exists(filePath))
{
using (var fileStreamReader = File.OpenText(filePath))
{
return await fileStreamReader.ReadToEndAsync();
}
}
else
{
throw new FileNotFoundException();
}
}
catch (IOException ex)
{
if (runCount == 3)
{
throw;
}
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
return null;
}
Для обработки поиска кэшированных файлов создается FileService
. Направленный в службу вызов метода GetFileContent
пытается получить содержимое файла из кэша в памяти и вернуть его вызывающему объекту (Services/FileService.cs
).
Если кэшированное содержимое не удается найти по ключу кэша, выполняются следующие действия:
- Содержимое файла получается с помощью
GetFileContent
. - Токен изменений извлекается из файла поставщика с помощью IFileProviders.Watch. При изменении файла активируется обратный вызов токена.
- Содержимое файла кэшируется с использованием скользящего срока действия. Токен изменений подключается к MemoryCacheEntryExtensions.AddExpirationToken для исключения записи кэша, если файл изменяется во время его кэширования.
В следующем примере файлы хранятся в корневом каталоге содержимого приложения. IWebHostEnvironment.ContentRootFileProvider
используется для получения IFileProvider с указанием на приложение IWebHostEnvironment.ContentRootPath
. Для получения filePath
используется IFileInfo.PhysicalPath.
public class FileService
{
private readonly IMemoryCache _cache;
private readonly IFileProvider _fileProvider;
private List<string> _tokens = new List<string>();
public FileService(IMemoryCache cache, IWebHostEnvironment env)
{
_cache = cache;
_fileProvider = env.ContentRootFileProvider;
}
public async Task<string> GetFileContents(string fileName)
{
var filePath = _fileProvider.GetFileInfo(fileName).PhysicalPath;
string fileContent;
// Try to obtain the file contents from the cache.
if (_cache.TryGetValue(filePath, out fileContent))
{
return fileContent;
}
// The cache doesn't have the entry, so obtain the file
// contents from the file itself.
fileContent = await GetFileContent(filePath);
if (fileContent != null)
{
// Obtain a change token from the file provider whose
// callback is triggered when the file is modified.
var changeToken = _fileProvider.Watch(fileName);
// Configure the cache entry options for a five minute
// sliding expiration and use the change token to
// expire the file in the cache if the file is
// modified.
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5))
.AddExpirationToken(changeToken);
// Put the file contents into the cache.
_cache.Set(filePath, fileContent, cacheEntryOptions);
return fileContent;
}
return string.Empty;
}
}
FileService
регистрируется в контейнере службы вместе со службой кэширования памяти.
В Startup.ConfigureServices
:
services.AddMemoryCache();
services.AddSingleton<FileService>();
Страничная модель загружает содержимое файла с помощью службы.
В методе страницы OnGet
индекса (Pages/Index.cshtml.cs
):
var fileContent = await _fileService.GetFileContents("poem.txt");
Класс CompositeChangeToken
Чтобы представить один или несколько экземпляров IChangeToken
в одном объекте, используйте класс CompositeChangeToken.
var firstCancellationTokenSource = new CancellationTokenSource();
var secondCancellationTokenSource = new CancellationTokenSource();
var firstCancellationToken = firstCancellationTokenSource.Token;
var secondCancellationToken = secondCancellationTokenSource.Token;
var firstCancellationChangeToken = new CancellationChangeToken(firstCancellationToken);
var secondCancellationChangeToken = new CancellationChangeToken(secondCancellationToken);
var compositeChangeToken =
new CompositeChangeToken(
new List<IChangeToken>
{
firstCancellationChangeToken,
secondCancellationChangeToken
});
HasChanged
для составного токена указывает true
, если HasChanged
любой из представленных токенов имеет значение true
. ActiveChangeCallbacks
для составного токена указывает true
, если ActiveChangeCallbacks
любой из представленных токенов имеет значение true
. Если возникает несколько одновременных событий изменения, обратный вызов составного изменения выполняется один раз.
Токен изменений — это низкоуровневый стандартный блок общего назначения, используемый для отслеживания изменений.
Просмотреть или скачать образец кода (описание загрузки)
Интерфейс IChangeToken
IChangeToken распространяет уведомления о том, что произошло изменение. IChangeToken
находится в пространстве имен Microsoft.Extensions.Primitives. Для приложений, которые не используют метапакет Microsoft.AspNetCore.App, создайте ссылку на пакет NuGet Microsoft.Extensions.Primitives.
IChangeToken
имеет два свойства:
- ActiveChangeCallbacks указывает, выполняет ли токен обратные вызовы с упреждением. Если для
ActiveChangedCallbacks
задано значениеfalse
, обратный вызов не выполняется, а приложению нужно опроситьHasChanged
на предмет изменений. Кроме того, отмена токена может никогда не произойти в случае отсутствия изменений или отключения либо удаления базового прослушивателя изменений. - HasChanged получает значение, указывающее, произошло ли изменение.
Интерфейс IChangeToken
включает метод RegisterChangeCallback(Action<Object>, Object), который регистрирует обратный вызов, выполняемый при изменении токена. Перед выполнением обратного вызова нужно задать HasChanged
.
Класс ChangeToken
ChangeToken — это статический класс, который распространяет уведомления о том, что произошло изменение. ChangeToken
находится в пространстве имен Microsoft.Extensions.Primitives. Для приложений, которые не используют метапакет Microsoft.AspNetCore.App, создайте ссылку на пакет NuGet Microsoft.Extensions.Primitives.
Метод ChangeToken.OnChange(Func<IChangeToken>, Action) регистрирует объект Action
, вызываемый при изменении токена:
Func<IChangeToken>
создает токен.Action
вызывается при изменении токена.
Перегрузка ChangeToken.OnChange<TState>>(Func<IChangeToken>, Action<TState>, TState) принимает дополнительный параметр TState
, передаваемый в объект-получатель токена Action
.
OnChange
возвращает IDisposable. При вызове Dispose токен прекращает прослушивать дальнейшие изменения и освобождает свои ресурсы.
Примеры использования токенов изменений в ASP.NET Core
Токены изменений используются в ключевых областях ASP.NET Core для отслеживания изменений в объектах:
- Чтобы отслеживать изменения в файлах, метод Watch интерфейса IFileProvider создает
IChangeToken
для указанных файлов или папки. - Токены
IChangeToken
можно добавлять в записи кэша, чтобы активировать вытеснения кэша при изменении. - Для изменений
TOptions
используемая по умолчанию реализация OptionsMonitor<TOptions> интерфейса IOptionsMonitor<TOptions> имеет перегрузку, которая принимает один или несколько экземпляров IOptionsChangeTokenSource<TOptions>. Каждый экземпляр возвращаетIChangeToken
, чтобы зарегистрировать обратный вызов уведомления об изменении для отслеживания изменений параметров.
Отслеживание изменений конфигурации
По умолчанию шаблоны ASP.NET Core используют файлы конфигурации JSON (appsettings.json
, appsettings.Development.json
и appsettings.Production.json
) для загрузки параметров конфигурации приложения.
Эти файлы настраиваются с помощью метода расширения AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean) в ConfigurationBuilder, который принимает параметр reloadOnChange
. reloadOnChange
указывает, нужно ли перезагружать конфигурацию при изменении файла. Этот параметр отображается в удобном методе CreateDefaultBuilder класса WebHost:
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true,
reloadOnChange: true);
Конфигурация на основе файла представлена FileConfigurationSource. FileConfigurationSource
использует IFileProvider для отслеживания файлов.
По умолчанию IFileMonitor
предоставляется объектом PhysicalFileProvider, который использует FileSystemWatcher для отслеживания изменений в файле конфигурации.
Этот пример приложения демонстрирует две реализации для отслеживания изменений конфигурации. Если любой из appsettings
файлов изменяется, обе реализации мониторинга файлов выполняют пользовательский код— пример приложения записывает сообщение в консоль.
FileSystemWatcher
файла конфигурации может активировать несколько обратных вызовов токена для одного изменения файла конфигурации. Чтобы пользовательский код выполнялся один раз при активации нескольких обратных вызовов токена, реализация в примере проверяет хэши файлов. В примере используется хэширование файлов SHA1. Повторная попытка реализуется посредством экспоненциальной задержки.
Utilities/Utilities.cs
:
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);
}
}
else
{
throw new FileNotFoundException();
}
}
catch (IOException ex)
{
if (runCount == 3)
{
throw;
}
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
return new byte[20];
}
Токен изменений для простого запуска
Зарегистрируйте обратный вызов Action
объекта-получателя токена для уведомлений об изменениях в токене перезагрузки конфигурации.
В Startup.Configure
:
ChangeToken.OnChange(
() => config.GetReloadToken(),
(state) => InvokeChanged(state),
env);
config.GetReloadToken()
предоставляет токен. Обратный вызов является методом InvokeChanged
:
private void InvokeChanged(IHostingEnvironment env)
{
byte[] appsettingsHash = ComputeHash("appSettings.json");
byte[] appsettingsEnvHash =
ComputeHash($"appSettings.{env.EnvironmentName}.json");
if (!_appsettingsHash.SequenceEqual(appsettingsHash) ||
!_appsettingsEnvHash.SequenceEqual(appsettingsEnvHash))
{
_appsettingsHash = appsettingsHash;
_appsettingsEnvHash = appsettingsEnvHash;
WriteConsole("Configuration changed (Simple Startup Change Token)");
}
}
state
обратного вызова используется для передачи IHostingEnvironment
. Это удобно для определения правильного файла конфигурации appsettings
, который требуется отслеживать (например, appsettings.Development.json
при работе в среде разработки). Хэши файлов используются для предотвращения многократного выполнения оператора WriteConsole
из-за нескольких обратных вызовов токена при всего одном изменении файла конфигурации.
Эта система выполняется, пока запущено приложение, и не может быть отключена пользователем.
Отслеживание изменений конфигурации как служба
Этот пример реализует следующее:
- Базовое отслеживание токена запуска.
- Отслеживание как служба.
- Механизм для включения и отключения отслеживания.
Этот пример задает интерфейс IConfigurationMonitor
.
Extensions/ConfigurationMonitor.cs
:
public interface IConfigurationMonitor
{
bool MonitoringEnabled { get; set; }
string CurrentState { get; set; }
}
Конструктор реализованного класса ConfigurationMonitor
регистрирует обратный вызов для уведомлений об изменениях:
public ConfigurationMonitor(IConfiguration config, IHostingEnvironment env)
{
_env = env;
ChangeToken.OnChange<IConfigurationMonitor>(
() => config.GetReloadToken(),
InvokeChanged,
this);
}
public bool MonitoringEnabled { get; set; } = false;
public string CurrentState { get; set; } = "Not monitoring";
config.GetReloadToken()
предоставляет токен. InvokeChanged
является методом обратного вызова. state
в этом экземпляре является ссылкой на экземпляр IConfigurationMonitor
, используемый для доступа к состоянию мониторинга. Используются два свойства:
MonitoringEnabled
: указывает, должен ли обратный вызов запускать его пользовательский код.CurrentState
: описывает текущее состояние мониторинга для использования в пользовательском интерфейсе.
Метод InvokeChanged
похож на описанный ранее подход, за исключением того, что он:
- не выполняет свой код, если только
MonitoringEnabled
не имеет значениеtrue
; - выводит текущее значение
state
в своих выходных данныхWriteConsole
.
private void InvokeChanged(IConfigurationMonitor state)
{
if (MonitoringEnabled)
{
byte[] appsettingsHash = ComputeHash("appSettings.json");
byte[] appsettingsEnvHash =
ComputeHash($"appSettings.{_env.EnvironmentName}.json");
if (!_appsettingsHash.SequenceEqual(appsettingsHash) ||
!_appsettingsEnvHash.SequenceEqual(appsettingsEnvHash))
{
string message = $"State updated at {DateTime.Now}";
_appsettingsHash = appsettingsHash;
_appsettingsEnvHash = appsettingsEnvHash;
WriteConsole("Configuration changed (ConfigurationMonitor Class) " +
$"{message}, state:{state.CurrentState}");
}
}
}
Экземпляр ConfigurationMonitor
регистрируется как служба в Startup.ConfigureServices
:
services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();
Страница индекса позволяет пользователю управлять отслеживанием конфигурации. Экземпляр IConfigurationMonitor
внедряется в IndexModel
.
Pages/Index.cshtml.cs
:
public IndexModel(
IConfiguration config,
IConfigurationMonitor monitor,
FileService fileService)
{
_config = config;
_monitor = monitor;
_fileService = fileService;
}
Монитор конфигурации (_monitor
) позволяет включить или отключить мониторинг и задать текущее состояние для обратной связи пользовательского интерфейса:
public IActionResult OnPostStartMonitoring()
{
_monitor.MonitoringEnabled = true;
_monitor.CurrentState = "Monitoring!";
return RedirectToPage();
}
public IActionResult OnPostStopMonitoring()
{
_monitor.MonitoringEnabled = false;
_monitor.CurrentState = "Not monitoring";
return RedirectToPage();
}
При активации OnPostStartMonitoring
отслеживание включается, а текущее состояние сбрасывается. При активации OnPostStopMonitoring
отслеживание отключается, а состояние указывает на отсутствие отслеживания.
Кнопки пользовательского интерфейса для включения и отключения отслеживания.
Pages/Index.cshtml
:
<button class="btn btn-success" asp-page-handler="StartMonitoring">
Start Monitoring
</button>
<button class="btn btn-danger" asp-page-handler="StopMonitoring">
Stop Monitoring
</button>
Отслеживание изменений кэшированных файлов
Содержимое файла можно кэшировать в памяти с помощью IMemoryCache. Кэширование в памяти описано в разделе Кэш в памяти. Устаревшие (просроченные) данные возвращаются из кэша при изменении источника исходных данных без каких-либо дополнительных действий, таких как описанная ниже реализация.
Например, если не учитывать состояние кэшированного исходного файла при продлении скользящего срока действия, это приведет к появлению в кэше устаревших данных файлов. Каждый запрос к данным продляет скользящий срок действия, но этот файл никогда не загружается в кэш. Любые функции приложения, использующие кэшированное содержимое, могут получить устаревшее содержимое.
Использование токенов изменений в сценарии кэширования файлов предотвращает появление устаревшего содержимого файлов в кэше. Этот пример демонстрирует реализацию данного подхода.
GetFileContent
в примере используется для:
- возврата содержимого файла;
- Реализуйте алгоритм повторных попыток с экспоненциальным обратным отключением, чтобы охватить случаи, когда проблема доступа к файлам временно задерживает чтение содержимого файла.
Utilities/Utilities.cs
:
public async static Task<string> GetFileContent(string filePath)
{
var runCount = 1;
while(runCount < 4)
{
try
{
if (File.Exists(filePath))
{
using (var fileStreamReader = File.OpenText(filePath))
{
return await fileStreamReader.ReadToEndAsync();
}
}
else
{
throw new FileNotFoundException();
}
}
catch (IOException ex)
{
if (runCount == 3 || ex.HResult != -2147024864)
{
throw;
}
else
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
}
return null;
}
Для обработки поиска кэшированных файлов создается FileService
. Направленный в службу вызов метода GetFileContent
пытается получить содержимое файла из кэша в памяти и вернуть его вызывающему объекту (Services/FileService.cs
).
Если кэшированное содержимое не удается найти по ключу кэша, выполняются следующие действия:
- Содержимое файла получается с помощью
GetFileContent
. - Токен изменений извлекается из файла поставщика с помощью IFileProviders.Watch. При изменении файла активируется обратный вызов токена.
- Содержимое файла кэшируется с использованием скользящего срока действия. Токен изменений подключается к MemoryCacheEntryExtensions.AddExpirationToken для исключения записи кэша, если файл изменяется во время его кэширования.
В следующем примере файлы хранятся в корневом каталоге содержимого приложения. IHostingEnvironment.ContentRootFileProvider используется для получения объекта IFileProvider, который указывает на ContentRootPath приложения. Для получения filePath
используется IFileInfo.PhysicalPath.
public class FileService
{
private readonly IMemoryCache _cache;
private readonly IFileProvider _fileProvider;
private List<string> _tokens = new List<string>();
public FileService(IMemoryCache cache, IHostingEnvironment env)
{
_cache = cache;
_fileProvider = env.ContentRootFileProvider;
}
public async Task<string> GetFileContents(string fileName)
{
var filePath = _fileProvider.GetFileInfo(fileName).PhysicalPath;
string fileContent;
// Try to obtain the file contents from the cache.
if (_cache.TryGetValue(filePath, out fileContent))
{
return fileContent;
}
// The cache doesn't have the entry, so obtain the file
// contents from the file itself.
fileContent = await GetFileContent(filePath);
if (fileContent != null)
{
// Obtain a change token from the file provider whose
// callback is triggered when the file is modified.
var changeToken = _fileProvider.Watch(fileName);
// Configure the cache entry options for a five minute
// sliding expiration and use the change token to
// expire the file in the cache if the file is
// modified.
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5))
.AddExpirationToken(changeToken);
// Put the file contents into the cache.
_cache.Set(filePath, fileContent, cacheEntryOptions);
return fileContent;
}
return string.Empty;
}
}
FileService
регистрируется в контейнере службы вместе со службой кэширования памяти.
В Startup.ConfigureServices
:
services.AddMemoryCache();
services.AddSingleton<FileService>();
Страничная модель загружает содержимое файла с помощью службы.
В методе страницы OnGet
индекса (Pages/Index.cshtml.cs
):
var fileContent = await _fileService.GetFileContents("poem.txt");
Класс CompositeChangeToken
Чтобы представить один или несколько экземпляров IChangeToken
в одном объекте, используйте класс CompositeChangeToken.
var firstCancellationTokenSource = new CancellationTokenSource();
var secondCancellationTokenSource = new CancellationTokenSource();
var firstCancellationToken = firstCancellationTokenSource.Token;
var secondCancellationToken = secondCancellationTokenSource.Token;
var firstCancellationChangeToken = new CancellationChangeToken(firstCancellationToken);
var secondCancellationChangeToken = new CancellationChangeToken(secondCancellationToken);
var compositeChangeToken =
new CompositeChangeToken(
new List<IChangeToken>
{
firstCancellationChangeToken,
secondCancellationChangeToken
});
HasChanged
для составного токена указывает true
, если HasChanged
любой из представленных токенов имеет значение true
. ActiveChangeCallbacks
для составного токена указывает true
, если ActiveChangeCallbacks
любой из представленных токенов имеет значение true
. Если возникает несколько одновременных событий изменения, обратный вызов составного изменения выполняется один раз.
Дополнительные ресурсы
ASP.NET Core