Dela via


Identifiera ändringar med ändringstoken i ASP.NET Core

Note

Det här är inte den senaste versionen av den här artikeln. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Warning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Important

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

En ändringstoken är ett allmänt byggblock på låg nivå som används för att spåra tillståndsändringar.

Visa eller ladda ned exempelkod (hur du laddar ned)

IChangeToken-gränssnitt

IChangeToken sprider meddelanden om att en ändring har inträffat. IChangeToken finns i Microsoft.Extensions.Primitives namnområdet. NuGet-paketet Microsoft.Extensions.Primitives tillhandahålls implicit till ASP.NET Core-apparna.

IChangeToken har två egenskaper:

  • ActiveChangeCallbacks ange om token proaktivt genererar återanrop. Om ActiveChangedCallbacks är inställt på false, kommer ett återanrop aldrig att anropas, och appen måste övervaka HasChanged för ändringar. Det är också möjligt att en token aldrig avbryts om inga ändringar inträffar eller om den underliggande ändringslyssnaren tas bort eller inaktiveras.
  • HasChanged tar emot ett värde som anger om en ändring har inträffat.

Gränssnittet IChangeToken innehåller metoden RegisterChangeCallback(Action<Object>, Object), som registrerar ett återanrop som anropas när token har ändrats. HasChanged måste anges innan återanropet anropas.

ChangeToken-klass

ChangeToken är en statisk klass som används för att sprida meddelanden om att en ändring har inträffat. ChangeToken finns i Microsoft.Extensions.Primitives namnområdet. NuGet-paketet Microsoft.Extensions.Primitives tillhandahålls implicit till ASP.NET Core-apparna.

Metoden ChangeToken.OnChange(Func<IChangeToken>, Action) registrerar ett Action anrop när token ändras:

  • Func<IChangeToken> genererar token.
  • Action anropas när token ändras.

Överlagringen ChangeToken.OnChange<TState>(Func<IChangeToken>, Action<TState>, TState) tar ytterligare TState en parameter som skickas till tokenkonsumenten Action.

OnChange returnerar ett IDisposable. Anrop Dispose hindrar token från att lyssna efter ytterligare ändringar och frigör tokens resurser.

Exempel på användning av ändringstoken i ASP.NET Core

Ändringstoken används i framträdande områden i ASP.NET Core för att övervaka ändringar i objekt:

  • För att övervaka ändringar i filer, skapar IFileProvider-metoden en Watch för de angivna filerna eller mappen att övervaka.
  • IChangeToken token kan läggas till i cacheposter för att utlösa cacheutrymningar vid ändring.
  • Vid TOptions-ändringar har standardimplementeringen OptionsMonitor<TOptions> av IOptionsMonitor<TOptions> en överlagrad metod som accepterar en eller flera IOptionsChangeTokenSource<TOptions>-instanser. Varje instans returnerar en IChangeToken för att registrera ett återanrop för meddelanden om förändringar i alternativ för spårning.

Övervaka konfigurationsändringar

Som standard använder ASP.NET Core-mallar JSON-konfigurationsfiler (appsettings.json, appsettings.Development.jsonoch appsettings.Production.json) för att läsa in appkonfigurationsinställningar.

Dessa filer konfigureras med tilläggsmetoden AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean)ConfigurationBuilder som accepterar en reloadOnChange parameter. reloadOnChange anger om konfigurationen ska läsas in igen vid filändringar. Den här inställningen visas i Host bekvämlighetsmetoden CreateDefaultBuilder:

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

Filbaserad konfiguration representeras av FileConfigurationSource. FileConfigurationSource använder IFileProvider för att övervaka filer.

Som standard IFileMonitor tillhandahålls av en PhysicalFileProvider, som använder FileSystemWatcher för att övervaka ändringar i konfigurationsfilen.

Exempelappen visar två implementeringar för övervakning av konfigurationsändringar. Om någon av appsettings filerna ändras kör båda implementeringarna av filövervakning anpassad kod – exempelappen skriver ett meddelande till konsolen.

En konfigurationsfilens FileSystemWatcher kan utlösa flera tokenåterkopplingar för en enda ändring av konfigurationsfilen. För att säkerställa att den anpassade koden bara körs en gång när flera tokenåteranrop utlöses kontrollerar exemplets implementering filhashvärden. Exemplet använder SHA1-filshashing. Ett nytt försök implementeras med en exponentiell fördröjning.

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

Enkel startändringstoken

Registrera en återanropsfunktion för tokenkonsumenten Action för ändringsmeddelanden för konfigurationens återläsningstoken.

I Startup.Configure:

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

config.GetReloadToken() tillhandahåller en token. Återanropet är InvokeChanged metoden:

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

Återanropet state används för att skicka in IWebHostEnvironment, vilket är användbart för att specificera rätt appsettings konfigurationsfil att övervaka (till exempel appsettings.Development.json i utvecklingsmiljön). Fil-hashvärden används för att förhindra att instruktionen WriteConsole körs flera gånger på grund av flera tokenåteranrop när konfigurationsfilen bara har ändrats en gång.

Det här systemet körs så länge appen körs och kan inte inaktiveras av användaren.

Övervaka konfigurationsändringar som en tjänst

Exemplet implementerar:

  • Grundläggande övervakning av starttoken.
  • Övervakning som en tjänst.
  • En mekanism för att aktivera och inaktivera övervakning.

Exemplet upprättar ett IConfigurationMonitor gränssnitt.

Extensions/ConfigurationMonitor.cs:

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

Konstruktorn för den implementerade klassen, ConfigurationMonitor, registrerar ett återanrop för ändringsmeddelanden:

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() tillhandahåller token. InvokeChanged är återanropsmetoden. state I denna instans är en referens till den IConfigurationMonitor instans som används för att få åtkomst till övervakningstillståndet. Två egenskaper används:

  • MonitoringEnabled: Anger om återanropet ska köra sin anpassade kod.
  • CurrentState: Beskriver det aktuella övervakningstillståndet för användning i användargränssnittet.

Metoden InvokeChanged liknar den tidigare metoden, förutom att den:

  • Kör inte koden om inte MonitoringEnabled är true.
  • Matar ut strömmen state i utdata 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}");
        }
    }
}

En instans ConfigurationMonitor registreras som en tjänst i Startup.ConfigureServices:

services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();

Sidan Index ger användaren kontroll över konfigurationsövervakningen. Instansen av IConfigurationMonitor injiceras i IndexModel.

Pages/Index.cshtml.cs:

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

Konfigurationsövervakaren (_monitor) används för att aktivera eller inaktivera övervakning och ange aktuellt tillstånd för feedback om användargränssnittet:

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

    return RedirectToPage();
}

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

    return RedirectToPage();
}

När OnPostStartMonitoring utlöses aktiveras övervakningen och det aktuella tillståndet rensas. När OnPostStopMonitoring utlöses inaktiveras övervakningen och tillståndet är inställt på att återspegla att övervakning inte sker.

Knappar i användargränssnittet aktiverar och inaktiverar övervakning.

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>

Övervaka cachelagrade filändringar

Filinnehåll kan cachelagras i minnet med hjälp av IMemoryCache. Cachelagring i minnet beskrivs under avsnittet Cache i minnet. Utan att vidta ytterligare steg, till exempel implementeringen som beskrivs nedan, returneras inaktuella data från en cache om källdata ändras.

Om du till exempel inte tar hänsyn till statusen för en cachelagrad källfil när du förnyar en glidande förfalloperiod leder det till inaktuella cachelagrade fildata. Varje begäran om data förnyar den glidande förfalloperioden, men filen läses aldrig in på nytt i cacheminnet. Alla appfunktioner som använder filens cachelagrade innehåll kan komma att få inaktuellt innehåll.

Om du använder ändringstoken i ett scenario med filcachelagring förhindrar du förekomst av inaktuellt filinnehåll i cacheminnet. Exempelappen visar en implementering av metoden.

Exemplet använder GetFileContent för att:

  • Returnera filinnehåll.
  • Implementera en återförsöksalgoritm med exponentiell säkerhetskopiering för att täcka fall där ett filåtkomstproblem tillfälligt fördröjer läsningen av filens innehåll.

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

En FileService skapas för att hantera cachelagrade filsökningar. Metodanropet GetFileContent för tjänsten försöker hämta filinnehåll från minnesintern cache och returnera det till anroparen (Services/FileService.cs).

Om cachelagrat innehåll inte hittas med hjälp av cachenyckeln vidtas följande åtgärder:

  1. Filinnehållet hämtas med hjälp av GetFileContent.
  2. En ändringstoken hämtas från filprovidern med IFileProviders.Watch. Tokenens återanrop utlöses när filen ändras.
  3. Filinnehållet cachelagras med en glidande förfalloperiod . Ändringstoken är kopplad till MemoryCacheEntryExtensions.AddExpirationToken för att ta bort cacheposten om filen ändras medan den cachelagras.

I följande exempel lagras filer i appens innehållsrot. IWebHostEnvironment.ContentRootFileProvider används för att hämta en IFileProvider pekare mot appens IWebHostEnvironment.ContentRootPath. filePath hämtas med 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 registreras i tjänstcontainern; tillsammans med minnescachetjänsten.

I Startup.ConfigureServices:

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

Sidmodellen läser in filens innehåll med hjälp av tjänsten.

I indexsidans OnGet metod (Pages/Index.cshtml.cs):

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

CompositeChangeToken-klass

Om du vill representera en eller flera IChangeToken instanser i ett enda objekt använder du CompositeChangeToken klassen .

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 på den sammansatta token rapporterar true om någon representerad token HasChanged är true. ActiveChangeCallbacks på den sammansatta token rapporterar true om någon representerad token ActiveChangeCallbacks är true. Om flera samtidiga ändringshändelser inträffar anropas den sammansatta ändringsåteranropet en gång.

En ändringstoken är ett allmänt byggblock på låg nivå som används för att spåra tillståndsändringar.

Visa eller ladda ned exempelkod (hur du laddar ned)

IChangeToken-gränssnitt

IChangeToken sprider meddelanden om att en ändring har inträffat. IChangeToken finns i Microsoft.Extensions.Primitives namnområdet. För appar som inte använder Microsoft.AspNetCore.App metapaket skapar du en paketreferens för NuGet-paketet Microsoft.Extensions.Primitives .

IChangeToken har två egenskaper:

  • ActiveChangeCallbacks ange om token proaktivt genererar återanrop. Om ActiveChangedCallbacks är inställt på false, kommer ett återanrop aldrig att anropas, och appen måste övervaka HasChanged för ändringar. Det är också möjligt att en token aldrig avbryts om inga ändringar inträffar eller om den underliggande ändringslyssnaren tas bort eller inaktiveras.
  • HasChanged tar emot ett värde som anger om en ändring har inträffat.

Gränssnittet IChangeToken innehåller metoden RegisterChangeCallback(Action<Object>, Object), som registrerar ett återanrop som anropas när token har ändrats. HasChanged måste anges innan återanropet anropas.

ChangeToken-klass

ChangeToken är en statisk klass som används för att sprida meddelanden om att en ändring har inträffat. ChangeToken finns i Microsoft.Extensions.Primitives namnområdet. För appar som inte använder Microsoft.AspNetCore.App metapaket skapar du en paketreferens för NuGet-paketet Microsoft.Extensions.Primitives .

Metoden ChangeToken.OnChange(Func<IChangeToken>, Action) registrerar ett Action anrop när token ändras:

  • Func<IChangeToken> genererar token.
  • Action anropas när token ändras.

Överlagringen ChangeToken.OnChange<TState>(Func<IChangeToken>, Action<TState>, TState) tar ytterligare TState en parameter som skickas till tokenkonsumenten Action.

OnChange returnerar ett IDisposable. Anrop Dispose hindrar token från att lyssna efter ytterligare ändringar och frigör tokens resurser.

Exempel på användning av ändringstoken i ASP.NET Core

Ändringstoken används i framträdande områden i ASP.NET Core för att övervaka ändringar i objekt:

  • För att övervaka ändringar i filer, skapar IFileProvider-metoden en Watch för de angivna filerna eller mappen att övervaka.
  • IChangeToken token kan läggas till i cacheposter för att utlösa cacheutrymningar vid ändring.
  • Vid TOptions-ändringar har standardimplementeringen OptionsMonitor<TOptions> av IOptionsMonitor<TOptions> en överlagrad metod som accepterar en eller flera IOptionsChangeTokenSource<TOptions>-instanser. Varje instans returnerar en IChangeToken för att registrera ett återanrop för meddelanden om förändringar i alternativ för spårning.

Övervaka konfigurationsändringar

Som standard använder ASP.NET Core-mallar JSON-konfigurationsfiler (appsettings.json, appsettings.Development.jsonoch appsettings.Production.json) för att läsa in appkonfigurationsinställningar.

Dessa filer konfigureras med tilläggsmetoden AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean)ConfigurationBuilder som accepterar en reloadOnChange parameter. reloadOnChange anger om konfigurationen ska läsas in igen vid filändringar. Den här inställningen visas i WebHost bekvämlighetsmetoden CreateDefaultBuilder:

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

Filbaserad konfiguration representeras av FileConfigurationSource. FileConfigurationSource använder IFileProvider för att övervaka filer.

Som standard IFileMonitor tillhandahålls av en PhysicalFileProvider, som använder FileSystemWatcher för att övervaka ändringar i konfigurationsfilen.

Exempelappen visar två implementeringar för övervakning av konfigurationsändringar. Om någon av appsettings filerna ändras kör båda implementeringarna av filövervakning anpassad kod – exempelappen skriver ett meddelande till konsolen.

En konfigurationsfilens FileSystemWatcher kan utlösa flera tokenåterkopplingar för en enda ändring av konfigurationsfilen. För att säkerställa att den anpassade koden bara körs en gång när flera tokenåteranrop utlöses kontrollerar exemplets implementering filhashvärden. Exemplet använder SHA1-filshashing. Ett nytt försök implementeras med en exponentiell fördröjning.

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

Enkel startändringstoken

Registrera en återanropsfunktion för tokenkonsumenten Action för ändringsmeddelanden för konfigurationens återläsningstoken.

I Startup.Configure:

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

config.GetReloadToken() tillhandahåller en token. Återanropet är InvokeChanged metoden:

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

Återanropet state används för att skicka in IHostingEnvironment, vilket är användbart för att specificera rätt appsettings konfigurationsfil att övervaka (till exempel appsettings.Development.json i utvecklingsmiljön). Fil-hashvärden används för att förhindra att instruktionen WriteConsole körs flera gånger på grund av flera tokenåteranrop när konfigurationsfilen bara har ändrats en gång.

Det här systemet körs så länge appen körs och kan inte inaktiveras av användaren.

Övervaka konfigurationsändringar som en tjänst

Exemplet implementerar:

  • Grundläggande övervakning av starttoken.
  • Övervakning som en tjänst.
  • En mekanism för att aktivera och inaktivera övervakning.

Exemplet upprättar ett IConfigurationMonitor gränssnitt.

Extensions/ConfigurationMonitor.cs:

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

Konstruktorn för den implementerade klassen, ConfigurationMonitor, registrerar ett återanrop för ändringsmeddelanden:

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() tillhandahåller token. InvokeChanged är återanropsmetoden. state I denna instans är en referens till den IConfigurationMonitor instans som används för att få åtkomst till övervakningstillståndet. Två egenskaper används:

  • MonitoringEnabled: Anger om återanropet ska köra sin anpassade kod.
  • CurrentState: Beskriver det aktuella övervakningstillståndet för användning i användargränssnittet.

Metoden InvokeChanged liknar den tidigare metoden, förutom att den:

  • Kör inte koden om inte MonitoringEnabled är true.
  • Matar ut strömmen state i utdata 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}");
        }
    }
}

En instans ConfigurationMonitor registreras som en tjänst i Startup.ConfigureServices:

services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();

Sidan Index ger användaren kontroll över konfigurationsövervakningen. Instansen av IConfigurationMonitor injiceras i IndexModel.

Pages/Index.cshtml.cs:

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

Konfigurationsövervakaren (_monitor) används för att aktivera eller inaktivera övervakning och ange aktuellt tillstånd för feedback om användargränssnittet:

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

    return RedirectToPage();
}

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

    return RedirectToPage();
}

När OnPostStartMonitoring utlöses aktiveras övervakningen och det aktuella tillståndet rensas. När OnPostStopMonitoring utlöses inaktiveras övervakningen och tillståndet är inställt på att återspegla att övervakning inte sker.

Knappar i användargränssnittet aktiverar och inaktiverar övervakning.

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>

Övervaka cachelagrade filändringar

Filinnehåll kan cachelagras i minnet med hjälp av IMemoryCache. Cachelagring i minnet beskrivs under avsnittet Cache i minnet. Utan att vidta ytterligare steg, till exempel implementeringen som beskrivs nedan, returneras inaktuella data från en cache om källdata ändras.

Om du till exempel inte tar hänsyn till statusen för en cachelagrad källfil när du förnyar en glidande förfalloperiod leder det till inaktuella cachelagrade fildata. Varje begäran om data förnyar den glidande förfalloperioden, men filen läses aldrig in på nytt i cacheminnet. Alla appfunktioner som använder filens cachelagrade innehåll kan komma att få inaktuellt innehåll.

Om du använder ändringstoken i ett scenario med filcachelagring förhindrar du förekomst av inaktuellt filinnehåll i cacheminnet. Exempelappen visar en implementering av metoden.

Exemplet använder GetFileContent för att:

  • Returnera filinnehåll.
  • Implementera en återförsöksalgoritm med exponentiell säkerhetskopiering för att täcka fall där ett filåtkomstproblem tillfälligt fördröjer läsningen av filens innehåll.

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

En FileService skapas för att hantera cachelagrade filsökningar. Metodanropet GetFileContent för tjänsten försöker hämta filinnehåll från minnesintern cache och returnera det till anroparen (Services/FileService.cs).

Om cachelagrat innehåll inte hittas med hjälp av cachenyckeln vidtas följande åtgärder:

  1. Filinnehållet hämtas med hjälp av GetFileContent.
  2. En ändringstoken hämtas från filprovidern med IFileProviders.Watch. Tokenens återanrop utlöses när filen ändras.
  3. Filinnehållet cachelagras med en glidande förfalloperiod . Ändringstoken är kopplad till MemoryCacheEntryExtensions.AddExpirationToken för att ta bort cacheposten om filen ändras medan den cachelagras.

I följande exempel lagras filer i appens innehållsrot. IHostingEnvironment.ContentRootFileProvider används för att hämta en IFileProvider som pekar på appens ContentRootPath. filePath hämtas med 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 registreras i tjänstcontainern; tillsammans med minnescachetjänsten.

I Startup.ConfigureServices:

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

Sidmodellen läser in filens innehåll med hjälp av tjänsten.

I indexsidans OnGet metod (Pages/Index.cshtml.cs):

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

CompositeChangeToken-klass

Om du vill representera en eller flera IChangeToken instanser i ett enda objekt använder du CompositeChangeToken klassen .

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 på den sammansatta token rapporterar true om någon representerad token HasChanged är true. ActiveChangeCallbacks på den sammansatta token rapporterar true om någon representerad token ActiveChangeCallbacks är true. Om flera samtidiga ändringshändelser inträffar anropas den sammansatta ändringsåteranropet en gång.

Ytterligare resurser