在 ASP.NET Core 中使用變更權杖來偵測變更
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
「變更權杖」是用來追蹤狀態變更的一般用途低階建置組塊。
檢視或下載範例程式碼 \(英文\) (如何下載)
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) 多載接受傳遞到權杖取用者 Action
的額外 TState
參數。
OnChange
會傳回 IDisposable。 呼叫 Dispose 將阻止權杖接聽其他變更和釋出權杖的資源。
ASP.NET Core 中變更權杖的範例用法
變更權杖用於 ASP.NET Core 監視物件變更的重要區域:
- 針對監視檔案變更,IFileProvider 的 Watch 方法會針對要監看的指定檔案或資料夾建立
IChangeToken
。 IChangeToken
權杖可新增至快取項目,以在變更時觸發快取收回。- 針對
TOptions
變更,IOptionsMonitor<TOptions> 的預設 OptionsMonitor<TOptions> 實作具有會接受一或多個 IOptionsChangeTokenSource<TOptions> 執行個體的多載。 每個執行個體會傳回IChangeToken
,以登錄變更通知回呼來追蹤選項變更。
監視設定變更
根據預設,ASP.NET Core 範本會使用 JSON 設定檔 (appsettings.json
、appsettings.Development.json
和 appsettings.Production.json
) 來載入應用程式組態設定。
這些檔案是在接受 reloadOnChange
參數的 ConfigurationBuilder 擴充方法上使用 AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean) 擴充方法來設定的。 reloadOnChange
指出組態是否應該在檔案變更時重新載入。 此設定會出在 Host 便利方法 CreateDefaultBuilder 中:
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
:描述要用於 UI 的目前監視狀態。
InvokeChanged
方法類似於之前的方法,不同之處在於:
- 不會執行其程式碼,除非
MonitoringEnabled
是true
。 - 在其
WriteConsole
輸出中輸出目前的state
。
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
) 是用來啟用或停用監視並設定 UI 回饋的目前狀態:
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
時,則會停用監視,且狀態會設定為反映未進行監視。
UI 啟用和停用監視中的按鈕。
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.AddExpirationToke,可在快取的檔案變更時收回快取項目。
在下列範例中,檔案是儲存在應用程式的內容根路徑。 IWebHostEnvironment.ContentRootFileProvider
是用來取得指向應用程式 IWebHostEnvironment.ContentRootPath
的 IFileProvider。 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 中繼套件的應用程式,請建立 Microsoft.Extensions.Primitives NuGet 套件的套件參考。
IChangeToken
有兩個屬性:
- ActiveChangeCallbacks 指出權杖是否主動引發回呼。 如果
ActiveChangedCallbacks
設定為false
,則絕不會呼叫回呼,而且應用程式必須輪詢HasChanged
是否有變更。 如果未發生任何變更,或基礎變更接聽程式已遭處置或停用,權杖也可能永遠不會被取消。 - HasChanged 會接收指出是否已發生變更的值。
IChangeToken
介面包括 RegisterChangeCallback(Action<Object>, Object) 方法,其會登錄在權杖變更時叫用的回呼。 HasChanged
必須在叫用回呼之前設定。
ChangeToken 類別
ChangeToken 靜態類別用來傳播已發生變更的通知。 ChangeToken
位於 Microsoft.Extensions.Primitives 命名空間內。 如需不使用 Microsoft.AspNetCore.App 中繼套件的應用程式,請建立 Microsoft.Extensions.Primitives NuGet 套件的套件參考。
ChangeToken.OnChange(Func<IChangeToken>, Action) 方法會登錄每當權杖變更時要呼叫的 Action
:
Func<IChangeToken>
會產生權杖。- 在權杖變更時呼叫
Action
。
ChangeToken.OnChange<TState>(Func<IChangeToken>, Action<TState>, TState) 多載接受傳遞到權杖取用者 Action
的額外 TState
參數。
OnChange
會傳回 IDisposable。 呼叫 Dispose 將阻止權杖接聽其他變更和釋出權杖的資源。
ASP.NET Core 中變更權杖的範例用法
變更權杖用於 ASP.NET Core 監視物件變更的重要區域:
- 針對監視檔案變更,IFileProvider 的 Watch 方法會針對要監看的指定檔案或資料夾建立
IChangeToken
。 IChangeToken
權杖可新增至快取項目,以在變更時觸發快取收回。- 針對
TOptions
變更,IOptionsMonitor<TOptions> 的預設 OptionsMonitor<TOptions> 實作具有會接受一或多個 IOptionsChangeTokenSource<TOptions> 執行個體的多載。 每個執行個體會傳回IChangeToken
,以登錄變更通知回呼來追蹤選項變更。
監視設定變更
根據預設,ASP.NET Core 範本會使用 JSON 設定檔 (appsettings.json
、appsettings.Development.json
和 appsettings.Production.json
) 來載入應用程式組態設定。
這些檔案是在接受 reloadOnChange
參數的 ConfigurationBuilder 擴充方法上使用 AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean) 擴充方法來設定的。 reloadOnChange
指出組態是否應該在檔案變更時重新載入。 此設定會出在 WebHost 便利方法 CreateDefaultBuilder 中:
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
:描述要用於 UI 的目前監視狀態。
InvokeChanged
方法類似於之前的方法,不同之處在於:
- 不會執行其程式碼,除非
MonitoringEnabled
是true
。 - 在其
WriteConsole
輸出中輸出目前的state
。
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
) 是用來啟用或停用監視並設定 UI 回饋的目前狀態:
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
時,則會停用監視,且狀態會設定為反映未進行監視。
UI 啟用和停用監視中的按鈕。
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.AddExpirationToke,可在快取的檔案變更時收回快取項目。
在下列範例中,檔案是儲存在應用程式的內容根路徑。 IHostingEnvironment.ContentRootFileProvider 是用來取得指向應用程式 ContentRootPath 的 IFileProvider。 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
。 如果發生多個同時變更事件,會叫用複合變更回呼一次。