Delen via


Wijzigingen detecteren met wijzigingstokens in ASP.NET Core

Note

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikel voor de huidige release.

Warning

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie het .NET- en .NET Core-ondersteuningsbeleid voor meer informatie. Zie de .NET 9-versie van dit artikel voor de huidige release.

Important

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikel voor de huidige release.

Een wijzigingstoken is een bouwsteen voor algemeen gebruik op laag niveau die wordt gebruikt om statuswijzigingen bij te houden.

Voorbeeldcode bekijken of downloaden (hoe download je)

Interface IChangeToken

IChangeToken hiermee worden meldingen doorgegeven dat er een wijziging is opgetreden. IChangeToken zich in de Microsoft.Extensions.Primitives naamruimte bevindt. Het NuGet-pakket Microsoft.Extensions.Primitives wordt impliciet geleverd aan de ASP.NET Core-apps.

IChangeToken heeft twee eigenschappen:

  • ActiveChangeCallbacks geeft aan of het token proactief callbacks genereert. Als ActiveChangedCallbacks is ingesteld op false, wordt er nooit een callback aangeroepen en moet de app HasChanged peilen op wijzigingen. Het is ook mogelijk dat een token nooit wordt geannuleerd als er geen wijzigingen optreden of als de onderliggende wijzigingslistener wordt verwijderd of uitgeschakeld.
  • HasChanged ontvangt een waarde die aangeeft of er een wijziging is opgetreden.

De IChangeToken interface bevat de methode RegisterChangeCallback(Action<Object>, Object), waarmee een callback wordt geregistreerd die wordt aangeroepen wanneer het token is gewijzigd. HasChanged moet worden ingesteld voordat de callback wordt aangeroepen.

ChangeToken-klasse

ChangeToken is een statische klasse die wordt gebruikt om meldingen door te geven die een wijziging heeft plaatsgevonden. ChangeToken zich in de Microsoft.Extensions.Primitives naamruimte bevindt. Het NuGet-pakket Microsoft.Extensions.Primitives wordt impliciet geleverd aan de ASP.NET Core-apps.

De methode ChangeToken.OnChange(Func<IChangeToken>, Action) registreert een Action aanroep wanneer het token wordt gewijzigd:

  • Func<IChangeToken> genereert het token.
  • Action wordt aangeroepen wanneer het token wordt gewijzigd.

De overbelasting ChangeToken.OnChange<TState>(Func<IChangeToken>, Action<TState, TState>) heeft een extra TState parameter die wordt doorgegeven aan de tokenconsumer Action.

OnChange retourneert een IDisposable. Het aanroepen van Dispose zorgt ervoor dat het token stopt met luisteren naar verdere wijzigingen en dat de resources van het token worden vrijgegeven.

Voorbeeld van het gebruik van wijzigingstokens in ASP.NET Core

Wijzigingstokens worden gebruikt in prominente gebieden van ASP.NET Core om te controleren op wijzigingen in objecten:

  • Voor het bewaken van wijzigingen in bestanden maakt IFileProviderde Watch methode een IChangeToken voor de opgegeven bestanden of map die u wilt bekijken.
  • IChangeToken tokens kunnen worden toegevoegd aan cachevermeldingen om verwijderingen in de cache te activeren bij wijziging.
  • Voor TOptions wijzigingen heeft de standaard OptionsMonitor<TOptions> implementatie van IOptionsMonitor<TOptions> een overload die één of meer IOptionsChangeTokenSource<TOptions> exemplaren accepteert. Elke instantie geeft een IChangeToken terug om een wijzigingsmeldingscallback te registreren voor het bijhouden van wijzigingen in opties.

Controleren op configuratiewijzigingen

Standaard gebruiken ASP.NET Core-sjablonen JSON-configuratiebestanden (appsettings.json, appsettings.Development.jsonen appsettings.Production.json) om app-configuratie-instellingen te laden.

Deze bestanden worden geconfigureerd met de extensiemethode ConfigurationBuilder die een reloadOnChange parameter accepteert. reloadOnChange geeft aan of de configuratie opnieuw moet worden geladen bij bestandswijzigingen. Deze instelling wordt weergegeven in de Host gemaksmethode CreateDefaultBuilder:

config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, 
          reloadOnChange: true);

Configuratie op basis van bestanden wordt vertegenwoordigd door FileConfigurationSource. FileConfigurationSource gebruikt IFileProvider voor het bewaken van bestanden.

Standaard wordt de IFileMonitor geleverd door een PhysicalFileProvider, die FileSystemWatcher gebruikt om wijzigingen in configuratiebestanden te monitoren.

De voorbeeld-app demonstreert twee implementaties voor het bewaken van configuratiewijzigingen. Als een van de appsettings bestanden wordt gewijzigd, voeren beide implementaties voor bestandsbewaking aangepaste code uit: de voorbeeld-app schrijft een bericht naar de console.

Een configuratiebestand FileSystemWatcher kan meerdere token callbacks activeren voor één configuratiebestandswijziging. Om ervoor te zorgen dat de aangepaste code slechts eenmaal wordt uitgevoerd wanneer meerdere token callbacks worden geactiveerd, controleert de implementatie bestands-hashes. Het voorbeeld maakt gebruik van SHA1-bestandshashing. Een nieuwe poging wordt ondernomen met een exponentieel toenemende wachttijd.

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

Token voor eenvoudige opstartveranderingen

Registreer een callback voor een token-consument Action voor wijzigingsmeldingen aan het herlaadtoken van de configuratie.

In Startup.Configure:

ChangeToken.OnChange(
    () => config.GetReloadToken(),
    (state) => InvokeChanged(state),
    env);

config.GetReloadToken() levert het token. De callback is de methode 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)");
    }
}

De state van de callback wordt gebruikt om het IWebHostEnvironment door te geven, wat handig is voor het specificeren van het juiste appsettings configuratiebestand dat moet worden bewaakt (bijvoorbeeld appsettings.Development.json wanneer in de ontwikkelingsomgeving). Bestands-hashes worden gebruikt om te voorkomen dat de WriteConsole instructie meerdere keren wordt uitgevoerd vanwege meerdere callbacks van tokens wanneer het configuratiebestand slechts eenmaal is gewijzigd.

Dit systeem wordt uitgevoerd zolang de app wordt uitgevoerd en niet kan worden uitgeschakeld door de gebruiker.

Configuratiewijzigingen als een service bewaken

In het voorbeeld wordt het volgende geïmplementeerd:

  • Eenvoudige opstarttokenbewaking.
  • Bewaking als een service.
  • Een mechanisme voor het in- en uitschakelen van bewaking.

Het voorbeeld brengt een IConfigurationMonitor interface tot stand.

Extensions/ConfigurationMonitor.cs:

public interface IConfigurationMonitor
{
    bool MonitoringEnabled { get; set; }
    string CurrentState { get; set; }
}

De constructor van de geïmplementeerde klasse, ConfigurationMonitorregistreert een callback voor wijzigingsmeldingen:

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() levert het token. InvokeChanged is de callback-methode. De state in dit geval is een verwijzing naar de IConfigurationMonitor instantie die wordt gebruikt voor toegang tot de monitoringstatus. Er worden twee eigenschappen gebruikt:

  • MonitoringEnabled: Geeft aan of de callback de aangepaste code moet uitvoeren.
  • CurrentState: Beschrijft de huidige bewakingsstatus voor gebruik in de gebruikersinterface.

De InvokeChanged methode is vergelijkbaar met de eerdere benadering, behalve dat deze:

  • De code wordt niet uitgevoerd tenzij MonitoringEnabledtrue is.
  • Voert de huidige state uit naar de WriteConsole uitvoer.
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}");
        }
    }
}

Een exemplaar ConfigurationMonitor wordt geregistreerd als een service in Startup.ConfigureServices:

services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();

De pagina Index biedt de gebruikerscontrole over configuratiebewaking. Het exemplaar van IConfigurationMonitor wordt geïnjecteerd in de IndexModel.

Pages/Index.cshtml.cs:

public IndexModel(
    IConfiguration config, 
    IConfigurationMonitor monitor, 
    FileService fileService)
{
    _config = config;
    _monitor = monitor;
    _fileService = fileService;
}

De configuratiemonitor (_monitor) wordt gebruikt om bewaking in of uit te schakelen en de huidige status voor feedback over de gebruikersinterface in te stellen:

public IActionResult OnPostStartMonitoring()
{
    _monitor.MonitoringEnabled = true;
    _monitor.CurrentState = "Monitoring!";

    return RedirectToPage();
}

public IActionResult OnPostStopMonitoring()
{
    _monitor.MonitoringEnabled = false;
    _monitor.CurrentState = "Not monitoring";

    return RedirectToPage();
}

Wanneer OnPostStartMonitoring wordt geactiveerd, wordt monitoring ingeschakeld en wordt de huidige status gewist. Wanneer OnPostStopMonitoring deze wordt geactiveerd, wordt bewaking uitgeschakeld en wordt de status ingesteld om aan te geven dat de bewaking niet plaatsvindt.

Knoppen in de gebruikersinterface schakelen bewaking in en uitschakelen.

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>

Bestandswijzigingen in cache bewaken

Bestandsinhoud kan in het geheugen worden opgeslagen met behulp van IMemoryCache. In-memory caching wordt beschreven in het onderwerp Cache in-memory . Zonder aanvullende stappen te nemen, zoals de hieronder beschreven implementatie, worden verouderde gegevens uit een cache geretourneerd als de brongegevens worden gewijzigd.

Als je bijvoorbeeld de status van een in de cache opgeslagen bronbestand niet in overweging neemt bij het vernieuwen van een glijdende verloopperiode, kan dit leiden tot verouderde bestandsgegevens in de cache. Elke aanvraag voor de gegevens verlengt de verloopperiode, maar het bestand wordt nooit opnieuw geladen in de cache. Alle app-functies die gebruikmaken van de inhoud in de cache van het bestand, zijn onderhevig aan mogelijk verouderde inhoud.

Als u wijzigingentokens gebruikt in een scenario met bestandscaching, voorkomt u dat verouderde bestandsinhoud in de cache aanwezig is. De voorbeeld-app demonstreert een implementatie van de aanpak.

In het voorbeeld wordt GetFileContent gebruikt om:

  • Bestandsinhoud retourneren.
  • Implementeer een algoritme voor opnieuw proberen met exponentieel uitstel om gevallen te behandelen waarin een probleem met bestandstoegang tijdelijk het lezen van de inhoud van het bestand vertraagt.

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

Er wordt een FileService gemaakt om opzoekacties voor bestanden in de cache af te handelen. De GetFileContent methode-aanroep van de service probeert bestandsinhoud te verkrijgen uit de cache in het geheugen en deze terug te keren naar de aanroeper (Services/FileService.cs).

Als inhoud in de cache niet wordt gevonden met behulp van de cachesleutel, worden de volgende acties uitgevoerd:

  1. De bestandsinhoud wordt verkregen met behulp van GetFileContent.
  2. Er wordt een wijzigingstoken verkregen van de bestandsprovider met IFileProviders.Watch. De callback van het token wordt geactiveerd wanneer het bestand wordt gewijzigd.
  3. De bestandsinhoud wordt in de cache opgeslagen met een verschuifbare verloopperiode . Het wijzigingstoken is gekoppeld aan MemoryCacheEntryExtensions.AddExpirationToken om de cachevermelding te verwijderen als het bestand wordt gewijzigd terwijl het in de cache is opgeslagen.

In het volgende voorbeeld worden bestanden opgeslagen in de hoofdmap van de inhoud van de app. IWebHostEnvironment.ContentRootFileProvider wordt gebruikt om een IFileProvider te verkrijgen die verwijst naar de IWebHostEnvironment.ContentRootPath van de app. De filePath wordt verkregen met 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;
    }
}

De FileService is geregistreerd in de servicecontainer, samen met de geheugen-cache-service.

In Startup.ConfigureServices:

services.AddMemoryCache();
services.AddSingleton<FileService>();

Het paginamodel laadt de inhoud van het bestand met behulp van de service.

In de methode OnGet van de indexpagina (Pages/Index.cshtml.cs):

var fileContent = await _fileService.GetFileContents("poem.txt");

Klasse CompositeChangeToken

Gebruik de IChangeToken klasse voor het weergeven van een of meer CompositeChangeToken exemplaren in één object.

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 in de rapporten van het samengestelde token true als een van de vertegenwoordigde tokens HasChanged is true. ActiveChangeCallbacks in de rapporten van het samengestelde token true als een van de vertegenwoordigde tokens ActiveChangeCallbacks is true. Als er meerdere gelijktijdige wijzigingsgebeurtenissen optreden, wordt de callback voor samengestelde wijzigingen eenmalig aangeroepen.

Een wijzigingstoken is een bouwsteen voor algemeen gebruik op laag niveau die wordt gebruikt om statuswijzigingen bij te houden.

Voorbeeldcode bekijken of downloaden (hoe download je)

Interface IChangeToken

IChangeToken hiermee worden meldingen doorgegeven dat er een wijziging is opgetreden. IChangeToken zich in de Microsoft.Extensions.Primitives naamruimte bevindt. Voor apps die niet gebruikmaken van het Microsoft.AspNetCore.App metapackage, maakt u een pakketverwijzing voor het NuGet-pakket Microsoft.Extensions.Primitives .

IChangeToken heeft twee eigenschappen:

  • ActiveChangeCallbacks geeft aan of het token proactief callbacks genereert. Als ActiveChangedCallbacks is ingesteld op false, wordt er nooit een callback aangeroepen en moet de app HasChanged peilen op wijzigingen. Het is ook mogelijk dat een token nooit wordt geannuleerd als er geen wijzigingen optreden of als de onderliggende wijzigingslistener wordt verwijderd of uitgeschakeld.
  • HasChanged ontvangt een waarde die aangeeft of er een wijziging is opgetreden.

De IChangeToken interface bevat de methode RegisterChangeCallback(Action<Object>, Object), waarmee een callback wordt geregistreerd die wordt aangeroepen wanneer het token is gewijzigd. HasChanged moet worden ingesteld voordat de callback wordt aangeroepen.

ChangeToken-klasse

ChangeToken is een statische klasse die wordt gebruikt om meldingen door te geven die een wijziging heeft plaatsgevonden. ChangeToken zich in de Microsoft.Extensions.Primitives naamruimte bevindt. Voor apps die niet gebruikmaken van het Microsoft.AspNetCore.App metapackage, maakt u een pakketverwijzing voor het NuGet-pakket Microsoft.Extensions.Primitives .

De methode ChangeToken.OnChange(Func<IChangeToken>, Action) registreert een Action aanroep wanneer het token wordt gewijzigd:

  • Func<IChangeToken> genereert het token.
  • Action wordt aangeroepen wanneer het token wordt gewijzigd.

De overbelasting ChangeToken.OnChange<TState>(Func<IChangeToken>, Action<TState, TState>) heeft een extra TState parameter die wordt doorgegeven aan de tokenconsumer Action.

OnChange retourneert een IDisposable. Het aanroepen van Dispose zorgt ervoor dat het token stopt met luisteren naar verdere wijzigingen en dat de resources van het token worden vrijgegeven.

Voorbeeld van het gebruik van wijzigingstokens in ASP.NET Core

Wijzigingstokens worden gebruikt in prominente gebieden van ASP.NET Core om te controleren op wijzigingen in objecten:

  • Voor het bewaken van wijzigingen in bestanden maakt IFileProviderde Watch methode een IChangeToken voor de opgegeven bestanden of map die u wilt bekijken.
  • IChangeToken tokens kunnen worden toegevoegd aan cachevermeldingen om verwijderingen in de cache te activeren bij wijziging.
  • Voor TOptions wijzigingen heeft de standaard OptionsMonitor<TOptions> implementatie van IOptionsMonitor<TOptions> een overload die één of meer IOptionsChangeTokenSource<TOptions> exemplaren accepteert. Elke instantie geeft een IChangeToken terug om een wijzigingsmeldingscallback te registreren voor het bijhouden van wijzigingen in opties.

Controleren op configuratiewijzigingen

Standaard gebruiken ASP.NET Core-sjablonen JSON-configuratiebestanden (appsettings.json, appsettings.Development.jsonen appsettings.Production.json) om app-configuratie-instellingen te laden.

Deze bestanden worden geconfigureerd met de extensiemethode ConfigurationBuilder die een reloadOnChange parameter accepteert. reloadOnChange geeft aan of de configuratie opnieuw moet worden geladen bij bestandswijzigingen. Deze instelling wordt weergegeven in de WebHost gemaksmethode CreateDefaultBuilder:

config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, 
          reloadOnChange: true);

Configuratie op basis van bestanden wordt vertegenwoordigd door FileConfigurationSource. FileConfigurationSource gebruikt IFileProvider voor het bewaken van bestanden.

Standaard wordt de IFileMonitor geleverd door een PhysicalFileProvider, die FileSystemWatcher gebruikt om wijzigingen in configuratiebestanden te monitoren.

De voorbeeld-app demonstreert twee implementaties voor het bewaken van configuratiewijzigingen. Als een van de appsettings bestanden wordt gewijzigd, voeren beide implementaties voor bestandsbewaking aangepaste code uit: de voorbeeld-app schrijft een bericht naar de console.

Een configuratiebestand FileSystemWatcher kan meerdere token callbacks activeren voor één configuratiebestandswijziging. Om ervoor te zorgen dat de aangepaste code slechts eenmaal wordt uitgevoerd wanneer meerdere token callbacks worden geactiveerd, controleert de implementatie bestands-hashes. Het voorbeeld maakt gebruik van SHA1-bestandshashing. Een nieuwe poging wordt ondernomen met een exponentieel toenemende wachttijd.

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

Token voor eenvoudige opstartveranderingen

Registreer een callback voor een token-consument Action voor wijzigingsmeldingen aan het herlaadtoken van de configuratie.

In Startup.Configure:

ChangeToken.OnChange(
    () => config.GetReloadToken(),
    (state) => InvokeChanged(state),
    env);

config.GetReloadToken() levert het token. De callback is de methode 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)");
    }
}

De state van de callback wordt gebruikt om het IHostingEnvironment door te geven, wat handig is voor het specificeren van het juiste appsettings configuratiebestand dat moet worden bewaakt (bijvoorbeeld appsettings.Development.json wanneer in de ontwikkelingsomgeving). Bestands-hashes worden gebruikt om te voorkomen dat de WriteConsole instructie meerdere keren wordt uitgevoerd vanwege meerdere callbacks van tokens wanneer het configuratiebestand slechts eenmaal is gewijzigd.

Dit systeem wordt uitgevoerd zolang de app wordt uitgevoerd en niet kan worden uitgeschakeld door de gebruiker.

Configuratiewijzigingen als een service bewaken

In het voorbeeld wordt het volgende geïmplementeerd:

  • Eenvoudige opstarttokenbewaking.
  • Bewaking als een service.
  • Een mechanisme voor het in- en uitschakelen van bewaking.

Het voorbeeld brengt een IConfigurationMonitor interface tot stand.

Extensions/ConfigurationMonitor.cs:

public interface IConfigurationMonitor
{
    bool MonitoringEnabled { get; set; }
    string CurrentState { get; set; }
}

De constructor van de geïmplementeerde klasse, ConfigurationMonitorregistreert een callback voor wijzigingsmeldingen:

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() levert het token. InvokeChanged is de callback-methode. De state in dit geval is een verwijzing naar de IConfigurationMonitor instantie die wordt gebruikt voor toegang tot de monitoringstatus. Er worden twee eigenschappen gebruikt:

  • MonitoringEnabled: Geeft aan of de callback de aangepaste code moet uitvoeren.
  • CurrentState: Beschrijft de huidige bewakingsstatus voor gebruik in de gebruikersinterface.

De InvokeChanged methode is vergelijkbaar met de eerdere benadering, behalve dat deze:

  • De code wordt niet uitgevoerd tenzij MonitoringEnabledtrue is.
  • Voert de huidige state uit naar de WriteConsole uitvoer.
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}");
        }
    }
}

Een exemplaar ConfigurationMonitor wordt geregistreerd als een service in Startup.ConfigureServices:

services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();

De pagina Index biedt de gebruikerscontrole over configuratiebewaking. Het exemplaar van IConfigurationMonitor wordt geïnjecteerd in de IndexModel.

Pages/Index.cshtml.cs:

public IndexModel(
    IConfiguration config, 
    IConfigurationMonitor monitor, 
    FileService fileService)
{
    _config = config;
    _monitor = monitor;
    _fileService = fileService;
}

De configuratiemonitor (_monitor) wordt gebruikt om bewaking in of uit te schakelen en de huidige status voor feedback over de gebruikersinterface in te stellen:

public IActionResult OnPostStartMonitoring()
{
    _monitor.MonitoringEnabled = true;
    _monitor.CurrentState = "Monitoring!";

    return RedirectToPage();
}

public IActionResult OnPostStopMonitoring()
{
    _monitor.MonitoringEnabled = false;
    _monitor.CurrentState = "Not monitoring";

    return RedirectToPage();
}

Wanneer OnPostStartMonitoring wordt geactiveerd, wordt monitoring ingeschakeld en wordt de huidige status gewist. Wanneer OnPostStopMonitoring deze wordt geactiveerd, wordt bewaking uitgeschakeld en wordt de status ingesteld om aan te geven dat de bewaking niet plaatsvindt.

Knoppen in de gebruikersinterface schakelen bewaking in en uitschakelen.

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>

Bestandswijzigingen in cache bewaken

Bestandsinhoud kan in het geheugen worden opgeslagen met behulp van IMemoryCache. In-memory caching wordt beschreven in het onderwerp Cache in-memory . Zonder aanvullende stappen te nemen, zoals de hieronder beschreven implementatie, worden verouderde gegevens uit een cache geretourneerd als de brongegevens worden gewijzigd.

Als je bijvoorbeeld de status van een in de cache opgeslagen bronbestand niet in overweging neemt bij het vernieuwen van een glijdende verloopperiode, kan dit leiden tot verouderde bestandsgegevens in de cache. Elke aanvraag voor de gegevens verlengt de verloopperiode, maar het bestand wordt nooit opnieuw geladen in de cache. Alle app-functies die gebruikmaken van de inhoud in de cache van het bestand, zijn onderhevig aan mogelijk verouderde inhoud.

Als u wijzigingentokens gebruikt in een scenario met bestandscaching, voorkomt u dat verouderde bestandsinhoud in de cache aanwezig is. De voorbeeld-app demonstreert een implementatie van de aanpak.

In het voorbeeld wordt GetFileContent gebruikt om:

  • Bestandsinhoud retourneren.
  • Implementeer een algoritme voor opnieuw proberen met exponentieel uitstel om gevallen te behandelen waarin een probleem met bestandstoegang tijdelijk het lezen van de inhoud van het bestand vertraagt.

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

Er wordt een FileService gemaakt om opzoekacties voor bestanden in de cache af te handelen. De GetFileContent methode-aanroep van de service probeert bestandsinhoud te verkrijgen uit de cache in het geheugen en deze terug te keren naar de aanroeper (Services/FileService.cs).

Als inhoud in de cache niet wordt gevonden met behulp van de cachesleutel, worden de volgende acties uitgevoerd:

  1. De bestandsinhoud wordt verkregen met behulp van GetFileContent.
  2. Er wordt een wijzigingstoken verkregen van de bestandsprovider met IFileProviders.Watch. De callback van het token wordt geactiveerd wanneer het bestand wordt gewijzigd.
  3. De bestandsinhoud wordt in de cache opgeslagen met een verschuifbare verloopperiode . Het wijzigingstoken is gekoppeld aan MemoryCacheEntryExtensions.AddExpirationToken om de cachevermelding te verwijderen als het bestand wordt gewijzigd terwijl het in de cache is opgeslagen.

In het volgende voorbeeld worden bestanden opgeslagen in de hoofdmap van de inhoud van de app. IHostingEnvironment.ContentRootFileProvider wordt gebruikt om een IFileProvider verwijzing naar de app ContentRootPathte verkrijgen. De filePath wordt verkregen met 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;
    }
}

De FileService is geregistreerd in de servicecontainer, samen met de geheugen-cache-service.

In Startup.ConfigureServices:

services.AddMemoryCache();
services.AddSingleton<FileService>();

Het paginamodel laadt de inhoud van het bestand met behulp van de service.

In de methode OnGet van de indexpagina (Pages/Index.cshtml.cs):

var fileContent = await _fileService.GetFileContents("poem.txt");

Klasse CompositeChangeToken

Gebruik de IChangeToken klasse voor het weergeven van een of meer CompositeChangeToken exemplaren in één object.

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 in de rapporten van het samengestelde token true als een van de vertegenwoordigde tokens HasChanged is true. ActiveChangeCallbacks in de rapporten van het samengestelde token true als een van de vertegenwoordigde tokens ActiveChangeCallbacks is true. Als er meerdere gelijktijdige wijzigingsgebeurtenissen optreden, wordt de callback voor samengestelde wijzigingen eenmalig aangeroepen.

Aanvullende bronnen