public async Task ConfigurationValueShouldNotChangeAfterChangeOnDb() { //Arrange DbConnection connection = new SqlConnection(_fixture.TestSettings.MssqlConnectionString); ConfigurationBuilder configuration = new ConfigurationBuilder(); configuration.Sources.Clear(); configuration.AddEFCoreConfiguration <PersonDbContext>( options => options.UseSqlServer(connection), reloadOnChange: true, pollingInterval: 500); //Act IConfigurationRoot configurationRoot = configuration.Build(); IChangeToken reloadToken = configurationRoot.GetReloadToken(); using var transaction = Connection.BeginTransaction(); using var context = CreateContext(transaction); SettingEntity setting = context.Find <SettingEntity>("TheValueWillBeChanged", "", ""); setting.Value = "This is the way!"; context.Update(setting); context.SaveChanges(); transaction.Commit(); await Task.Delay(1000); //Assert Assert.False(reloadToken.HasChanged); Assert.Equal("This is the way!", configurationRoot.GetValue <string>("TheValueWillBeChanged")); }
public TracingSetting(IConfiguration configuration) { _configuration = configuration; this.ChangeToken = _configuration.GetReloadToken(); this.ChangeToken.RegisterChangeCallback(this.ResolveConf, null); this.ResolveConf(null); }
public static async Task WaitForChange(this IChangeToken changeToken, int millisecondTimeout) { var tcs = new TaskCompletionSource <IChangeToken>(); IDisposable waitForChange = null; CancellationTokenSource ct = null; ct = new CancellationTokenSource(millisecondTimeout); ct.Token.Register(() => tcs.TrySetException(new TimeoutException()), useSynchronizationContext: false); waitForChange = changeToken.RegisterChangeCallback(_ => tcs.TrySetResult(changeToken), null); await tcs.Task; if (ct != null) { ct.Dispose(); ct = null; } if (waitForChange != null) { waitForChange.Dispose(); waitForChange = null; } }
public IChangeToken Watch() { consulConfigurationTokenSource = new CancellationTokenSource(); changeToken = new CancellationChangeToken(consulConfigurationTokenSource.Token); return(changeToken); }
public IEnumerable <FeaturedMovies> GetFeaturedMovies(out IChangeToken expirationToken) { _featuredMoviesTokenSource = new CancellationTokenSource(); expirationToken = new CancellationChangeToken(_featuredMoviesTokenSource.Token); return(GetMovies().OrderBy(m => m.Rank).Take(2)); }
public EventBusSettings(IConfiguration configuration, ILogger logger) { _logger = logger; _configuration = configuration; this.ChangeToken = _configuration.GetReloadToken(); List <EventBusHostOptions> hostsOptions = new List <EventBusHostOptions>(); ConfigurationBinder.Bind(_configuration.GetSection("Hosts"), hostsOptions); this.Hosts = hostsOptions.ToDictionary(p => p.Name, p => p); List <ProductOptions> productOptions = new List <ProductOptions>(); ConfigurationBinder.Bind(_configuration.GetSection("Products"), productOptions); this.Products = productOptions.ToDictionary(p => p.Id, p => p); EventBusConfigOptions configOptions = new EventBusConfigOptions(); ConfigurationBinder.Bind(_configuration.GetSection("Options"), configOptions); this.Options = configOptions; _logger.LogInformation("init EventBus.Hosts:" + this.Hosts.ToJson()); _logger.LogInformation("init EventBus.Products:" + this.Products.ToJson()); _logger.LogInformation("init EventBus.Options:" + this.Options.ToJson()); }
/// <summary> /// Create observer for one file. /// </summary> /// <param name="filesystem"></param> /// <param name="patternInfo"></param> /// <param name="observer"></param> /// <param name="state"></param> /// <param name="eventDispatcher"></param> public PatternObserver(IFileSystem filesystem, GlobPatternInfo patternInfo, IObserver <IEvent> observer, object state, IEventDispatcher eventDispatcher = default) : base(filesystem, patternInfo.Pattern, observer, state, eventDispatcher) { this.changeToken = FileProvider.Watch(patternInfo.Pattern); this.previousSnapshot = ReadSnapshot(); this.watcher = changeToken.RegisterChangeCallback(OnEvent, this); }
public Func <IChangeToken> Build(out IDisposable factoryLifetime) { var disposables = _disposables.ToArray(); // capture snapshot. factoryLifetime = new InvokeOnDispose(() => { foreach (var item in disposables) { item?.Dispose(); } }); if (Factories == null || Factories.Count == 0) { return(() => EmptyChangeToken.Instance); } if (Factories.Count == 1) { return(Factories[0]); // pass through - no need to build composite for single producer. } var factories = Factories.ToArray(); // capture snapshot Reset(); // so builder is empty again to build another. return(() => { var tokens = new IChangeToken[factories.Length]; for (var i = 0; i < factories.Length; i++) { tokens[i] = factories[i].Invoke(); } return new CompositeChangeToken(tokens); }); }
public void Include_ChangeToken() { var sut = new CompositeChangeTokenFactoryBuilder(); TriggerChangeToken token = null; var factory = sut.Include(() => { token = new TriggerChangeToken(); return(token); }).Build(out var lifetime); var consumed = factory(); Assert.Same(token, consumed); // When we trigger the token and request a new one, //we get a new one thats different from the previous one. IChangeToken newToken = null; IChangeToken original = token; token.RegisterChangeCallback(a => newToken = factory(), null); token.Trigger(); // await Task.Delay(200); Assert.NotNull(newToken); Assert.NotSame(newToken, original); }
internal IChangeToken GetOrAddFilePathChangeToken(string filePath) { if (!_filePathTokenLookup.TryGetValue(filePath, out var tokenInfo)) { var cancellationTokenSource = new CancellationTokenSource(); var cancellationChangeToken = new CancellationChangeToken(cancellationTokenSource.Token); tokenInfo = new ChangeTokenInfo(cancellationTokenSource, cancellationChangeToken); tokenInfo = _filePathTokenLookup.GetOrAdd(filePath, tokenInfo); } IChangeToken changeToken = tokenInfo.ChangeToken; if (PollForChanges) { // The expiry of CancellationChangeToken is controlled by this type and consequently we can cache it. // PollingFileChangeToken on the other hand manages its own lifetime and consequently we cannot cache it. var pollingChangeToken = new PollingFileChangeToken(new FileInfo(Path.Combine(_root, filePath))); if (UseActivePolling) { pollingChangeToken.ActiveChangeCallbacks = true; pollingChangeToken.CancellationTokenSource = new CancellationTokenSource(); PollingChangeTokens.TryAdd(pollingChangeToken, pollingChangeToken); } changeToken = new CompositeChangeToken( new[] { changeToken, pollingChangeToken, }); } return(changeToken); }
/// <summary> /// Inlcude your own change token's in the composite that are generated /// from the supplied <see cref="Func{CancellationToken}"/> and signalled when the cancellation tokens are signalled. /// If your <see cref="Func{CancellationToken}"/> at any point returns null, /// then an <see cref="EmptyChangeToken"/> will be returned to the consumer, /// which is a Noop token to avoid null ref exceptions. /// </summary> /// <param name="trigger"></param> /// <returns></returns> /// <summary> /// Inlcude your own change token's in the composite that are generated /// from the supplied <see cref="Func{CancellationToken}"/> and signalled when the cancellation tokens are signalled. /// If your <see cref="Func{CancellationToken}"/> at any point returns null, /// then an <see cref="EmptyChangeToken"/> will be returned to the consumer, /// which is a Noop token to avoid null ref exceptions. /// </summary> /// <param name="trigger"></param> /// <returns></returns> public CompositeChangeTokenFactoryBuilder IncludeCancellationTokens(Func <CancellationToken> cancellationTokenFactory) { IChangeToken factory() { var cancelToken = cancellationTokenFactory(); if (cancelToken == null) { return(EmptyChangeToken.Instance); } else { return(new CancellationChangeToken(cancelToken)); } } IChangeToken currentToken = null; IChangeToken result() { // consumer is asking for a new token, any previous token is dead. _ = Interlocked.Exchange(ref currentToken, factory()); return(currentToken); } Factories.Add(result); return(this); }
public ProxyConfigManager( ILogger <ProxyConfigManager> logger, IProxyConfigProvider provider, IClusterManager clusterManager, IRouteManager routeManager, IEnumerable <IProxyConfigFilter> filters, IConfigValidator configValidator, ProxyEndpointFactory proxyEndpointFactory, ITransformBuilder transformBuilder, IProxyHttpClientFactory httpClientFactory, IActiveHealthCheckMonitor activeHealthCheckMonitor) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _provider = provider ?? throw new ArgumentNullException(nameof(provider)); _clusterManager = clusterManager ?? throw new ArgumentNullException(nameof(clusterManager)); _routeManager = routeManager ?? throw new ArgumentNullException(nameof(routeManager)); _filters = filters ?? throw new ArgumentNullException(nameof(filters)); _configValidator = configValidator ?? throw new ArgumentNullException(nameof(configValidator)); _proxyEndpointFactory = proxyEndpointFactory ?? throw new ArgumentNullException(nameof(proxyEndpointFactory)); _transformBuilder = transformBuilder ?? throw new ArgumentNullException(nameof(transformBuilder)); _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); _activeHealthCheckMonitor = activeHealthCheckMonitor ?? throw new ArgumentNullException(nameof(activeHealthCheckMonitor)); _conventions = new List <Action <EndpointBuilder> >(); DefaultBuilder = new ReverseProxyConventionBuilder(_conventions); _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token); }
private IChangeToken GetOrAddWildcardChangeToken(string pattern) { ChangeTokenInfo tokenInfo; if (!_wildcardTokenLookup.TryGetValue(pattern, out tokenInfo)) { var cancellationTokenSource = new CancellationTokenSource(); var cancellationChangeToken = new CancellationChangeToken(cancellationTokenSource.Token); var matcher = new Matcher(StringComparison.OrdinalIgnoreCase); matcher.AddInclude(pattern); tokenInfo = new ChangeTokenInfo(cancellationTokenSource, cancellationChangeToken, matcher); tokenInfo = _wildcardTokenLookup.GetOrAdd(pattern, tokenInfo); } IChangeToken changeToken = tokenInfo.ChangeToken; if (_pollForChanges) { // The expiry of CancellationChangeToken is controlled by this type and consequently we can cache it. // PollingFileChangeToken on the other hand manages its own lifetime and consequently we cannot cache it. changeToken = new CompositeChangeToken( new[] { changeToken, new PollingWildCardChangeToken(_root, pattern) }); } return(changeToken); }
public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard) { // Arrange using var rootOfFile = new DisposableFileSystem(); string filePath = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); File.WriteAllText(filePath, "v1.1"); using var rootOfLink = new DisposableFileSystem(); string linkName = Path.GetRandomFileName(); string linkPath = Path.Combine(rootOfLink.RootPath, linkName); File.CreateSymbolicLink(linkPath, filePath); string filter = useWildcard ? "*" : linkName; using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(filter); var tcs = new TaskCompletionSource(); token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); // Act File.Delete(linkPath); // Assert Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}."); }
void Watch() { IChangeToken t = _root.GetReloadToken(); t.ActiveChangeCallbacks.Should().BeTrue(); t.RegisterChangeCallback(SetChange, null); }
public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetNotExists(bool useWildcard) { // Arrange using var rootOfLink = new TempDirectory(GetTestFilePath()); string linkName = GetTestFileName(); string linkPath = Path.Combine(rootOfLink.Path, linkName); File.CreateSymbolicLink(linkPath, "not-existent-file"); // Act using var provider = new PhysicalFileProvider(rootOfLink.Path) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); var tcs = new TaskCompletionSource(); token.RegisterChangeCallback(_ => { Assert.True(false, "Change event was raised when it was not expected."); }, null); var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); cts.Token.Register(() => tcs.TrySetCanceled()); await Assert.ThrowsAsync <TaskCanceledException>(() => tcs.Task); }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { var path = Href; // Get the value from the cache, or compute the value and add it to the cache var fileContent = await Cache.GetOrCreateAsync("InlineStyleTagHelper-" + path, async entry => { IFileProvider fileProvider = HostingEnvironment.WebRootFileProvider; IChangeToken changeToken = fileProvider.Watch(path); entry.SetPriority(CacheItemPriority.NeverRemove); entry.AddExpirationToken(changeToken); IFileInfo file = fileProvider.GetFileInfo(path); if (file == null || !file.Exists) { return(null); } return(await ReadFileContent(file)); }); if (fileContent == null) { output.SuppressOutput(); return; } output.TagName = "style"; output.Attributes.RemoveAll("href"); output.Content.AppendHtml(fileContent); }
/// <summary> /// Adds the specified key and value to the cache. /// </summary> /// <param name="key">The key of the value.</param> /// <param name="value">The value to store.</param> /// <param name="expiresIn">Optional expiration of the value.</param> /// <param name="expirationToken">Optional <see cref="IChangeToken"/> that causes the cache entry to expire.</param> public void Set(string key, object value, TimeSpan?expiresIn = null, IChangeToken expirationToken = null) { var options = new MemoryCacheEntryOptions(); options.SetAbsoluteExpiration(expiresIn ?? _options.DefaultExpiration); options.AddExpirationToken(new CancellationChangeToken(_tokenSource.Token)); if (expirationToken != null) { options.AddExpirationToken(expirationToken); } options.RegisterPostEvictionCallback((k, v, r, s) => { if (r == EvictionReason.Replaced) { return; } CleanupKeys(); TryRemoveKey(k.ToString()); }); _memoryCache.Set(key, value, options); _keys.TryAdd(key, value: true); }
public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) { // Arrange using var rootOfFile = new DisposableFileSystem(); string filePath = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); File.WriteAllText(filePath, "v1.1"); using var rootOfLink = new DisposableFileSystem(); string linkName = Path.GetRandomFileName(); string linkPath = Path.Combine(rootOfLink.RootPath, linkName); File.CreateSymbolicLink(linkPath, filePath); using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); var tcs = new TaskCompletionSource(); token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); // Act await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); // Assert Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}."); }
private async Task BindAsync(CancellationToken cancellationToken) { await _bindSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { if (_stopping == 1) { throw new InvalidOperationException("Kestrel has already been stopped."); } IChangeToken reloadToken = null; _serverAddresses.InternalCollection.PreventPublicMutation(); if (Options.ConfigurationLoader?.ReloadOnChange == true && (!_serverAddresses.PreferHostingUrls || _serverAddresses.InternalCollection.Count == 0)) { reloadToken = Options.ConfigurationLoader.Configuration.GetReloadToken(); } Options.ConfigurationLoader?.Load(); await AddressBinder.BindAsync(Options.ListenOptions, AddressBindContext).ConfigureAwait(false); _configChangedRegistration = reloadToken?.RegisterChangeCallback(async state => await((KestrelServerImpl)state).RebindAsync(), this); } finally { _bindSemaphore.Release(); } }
private void InitializeOutputs() { if (_outputInitialized && _outputChangeToken != null && !_outputChangeToken.HasChanged) { return; } _logger.LogInformation("Loading output configuration."); _outputInitialized = true; _outputChangeToken = _outputsConfiguration.GetReloadToken(); _outputs.Clear(); _outputs.AddRange(_outputsConfiguration .GetChildren() .Select(x => { try { return(ActivatorUtilities.CreateInstance <OutputDispatch>(_serviceProvider, x)); } catch (Exception e) { _logger.LogError(e, "Error loading output."); return(null); } }) .Where(x => x != null) .ToList()); }
public IChangeToken Watch() { _cancellationTokenSource = new CancellationTokenSource(); _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token); return(_changeToken); }
private IChangeToken GetOrAddFilePathChangeToken(string filePath) { ChangeTokenInfo tokenInfo; if (!_filePathTokenLookup.TryGetValue(filePath, out tokenInfo)) { var cancellationTokenSource = new CancellationTokenSource(); var cancellationChangeToken = new CancellationChangeToken(cancellationTokenSource.Token); tokenInfo = new ChangeTokenInfo(cancellationTokenSource, cancellationChangeToken); tokenInfo = _filePathTokenLookup.GetOrAdd(filePath, tokenInfo); } IChangeToken changeToken = tokenInfo.ChangeToken; if (_pollForChanges) { // The expiry of CancellationChangeToken is controlled by this type and consequently we can cache it. // PollingFileChangeToken on the other hand manages its own lifetime and consequently we cannot cache it. changeToken = new CompositeChangeToken( new[] { changeToken, new PollingFileChangeToken(new FileInfo(Path.Combine(_root, filePath))) }); } return(changeToken); }
public void PhysicalFileProvider_MoniteFile_MoniteOneTime() { var isCallBackInvoke = false; var provider = new PhysicalFileProvider(_appBasePath); IChangeToken token = provider.Watch("appsettings.json"); // 没有进行文件更改 token.RegisterChangeCallback(_ => isCallBackInvoke = true, null); Task.Delay(TimeSpan.FromSeconds(2)).Wait(); // 等待回调执行完成 Assert.False(isCallBackInvoke); Assert.False(token.HasChanged); // 进行了文件更改 var filePath = Path.Combine(provider.Root, "appsettings.json"); var fileContent = File.ReadAllText(filePath); File.WriteAllText(filePath, DateTime.Now.ToString(CultureInfo.InvariantCulture)); Task.Delay(TimeSpan.FromSeconds(2)).Wait(); // 等待回调执行完成 Assert.True(isCallBackInvoke); Assert.True(token.HasChanged); // 只能监控一次 isCallBackInvoke = false; File.WriteAllText(filePath, DateTime.Now.ToString(CultureInfo.InvariantCulture)); Task.Delay(TimeSpan.FromSeconds(2)).Wait(); // 等待回调执行完成 Assert.False(isCallBackInvoke); // !! Assert.True(token.HasChanged); File.WriteAllText(filePath, fileContent); // Token.HasChanged没有set方法, 所以只能监控一次 //token.HasChanged = false; }
public ScriptApplicationHostOptionsChangeTokenSource(IOptionsMonitor <StandbyOptions> standbyOptions) { _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token); _standbyOptionsOnChangeSubscription = standbyOptions.OnChange(o => { if (_cancellationTokenSource == null) { return; } // This should only ever happen once, on specialization, so null everything out // when this fires. var tokenSource = Interlocked.Exchange(ref _cancellationTokenSource, null); if (tokenSource != null && !tokenSource.IsCancellationRequested) { var changeToken = Interlocked.Exchange(ref _changeToken, NullChangeToken.Singleton); tokenSource.Cancel(); // Dispose of the token source so our change // token reflects that state tokenSource.Dispose(); } }); }
public static bool Stop() { // MAINT: Ditto the note at the top of Enable(). try { m_log?.Log("XPNet CLR: Stop"); m_plugin.Dispose(); m_plugin = null; m_api.Dispose(); m_api = null; m_configReloadTokenDisposer.Dispose(); m_configReloadTokenDisposer = null; m_configReloadToken = null; m_config = null; ApiFunctions = new ApiFunctions(); return(true); } catch (Exception exc) { m_log?.Log(exc); return(false); } }
public CssVisitorEditNameSelector(IFactoryNames factoryNames, ITokenFactory factoryTokens) { this.factoryNames = factoryNames; this.factoryTokens = factoryTokens; editId = new CssEditNameIdSelector(factoryTokens, factoryNames); editClass = new CssEditNameClassSelector(factoryTokens, factoryNames); }
public IEnumerable<FeaturedMovies> GetFeaturedMovies(out IChangeToken expirationToken) { _featuredMoviesTokenSource = new CancellationTokenSource(); expirationToken = new CancellationChangeToken(_featuredMoviesTokenSource.Token); return GetMovies().OrderBy(m => m.Rank).Take(2); }
/// <summary> /// Adds an object to distributed cache /// </summary> /// <typeparam name="TItem">Cache object type</typeparam> /// <param name="partitionName">Cache Partition Name</param> /// <param name="key">Cache Key</param> /// <param name="value">Cache object</param> /// <param name="expirationToken">Cache <see cref="IChangeToken"/> expiration token to be used while adding cache item</param> /// <param name="postEvictionCallback"><see cref="PostEvictionCallbackRegistration"/> delegate</param> public void Set <TItem>(string partitionName, string key, TItem value, IChangeToken expirationToken = null, PostEvictionCallbackRegistration postEvictionCallback = null) { var partition = GetPartition(partitionName); partition.Cache.Set(partitionName + key, value, GetDistributedCacheEntryOptions(partition, expirationToken, postEvictionCallback)); }
public InterceptionSetting(IConfiguration configuration) { this.configuration = configuration; this.ChangeToken = configuration.GetReloadToken(); this.ChangeToken.RegisterChangeCallback(_ => this.ReadyConf(), null); this.ReadyConf(); }
public IChangeToken GetReloadToken() { if (changeToken == null) { changeToken = new SqlDatabaseChangeToken(); } return(changeToken); }
/// <summary> /// Expire the cache entry if the given <see cref="IChangeToken"/> expires. /// </summary> /// <param name="options">The <see cref="MemoryCacheEntryOptions"/>.</param> /// <param name="expirationToken">The <see cref="IChangeToken"/> that causes the cache entry to expire.</param> public static MemoryCacheEntryOptions AddExpirationToken( this MemoryCacheEntryOptions options, IChangeToken expirationToken) { if (expirationToken == null) { throw new ArgumentNullException(nameof(expirationToken)); } options.ExpirationTokens.Add(expirationToken); return options; }
public string GetProducts(string category, out IChangeToken changeToken) { var token = _tokenSource.IsCancellationRequested ? CancellationToken.None : _tokenSource.Token; changeToken = new CancellationChangeToken(token); if (category == "Books") { return "Book1, Book2"; } else { return "Laptops"; } }
public string GetCriticsQuote(out IChangeToken expirationToken) { _quotesTokenSource = new CancellationTokenSource(); var quotes = new[] { "A must see for iguana lovers everywhere", "Slightly better than watching paint dry", "Never felt more relieved seeing the credits roll", "Bravo!" }; expirationToken = new CancellationChangeToken(_quotesTokenSource.Token); return quotes[_random.Next(0, quotes.Length)]; }
public async Task CreatedToken_Same_For_A_File_And_Callsback_AllRegisteredTokens_OnChange() { var fileName = Guid.NewGuid().ToString(); var fileLocation = Path.Combine(Path.GetTempPath(), fileName); File.WriteAllText(fileLocation, "Content"); var provider = new PhysicalFileProvider(Path.GetTempPath()); var count = 10; var tasks = new List<Task>(count); var tokens = new IChangeToken[count]; var callbackResults = new bool[count]; for (int i = 0; i < count; i++) { tasks.Add(new Task(index => { var changeToken = provider.Watch(fileName); tokens[(int)index] = changeToken; Assert.NotNull(changeToken); Assert.False(changeToken.HasChanged); changeToken.RegisterChangeCallback(_ => { callbackResults[(int)index] = true; }, index); }, state: i)); } // Simulating multiple concurrent requests to the same file. Parallel.ForEach(tasks, task => task.Start()); await Task.WhenAll(tasks); File.AppendAllText(fileLocation, "UpdatedContent"); // Some warm up time for the callbacks to be fired. await Task.Delay(WaitTimeForTokenToFire); for (int index = 1; index < count; index++) { Assert.Equal(tokens[index - 1], tokens[index]); } Assert.True(callbackResults.All(c => c)); File.Delete(fileLocation); }
public ChangeTokenInfo(IChangeToken changeToken, CancellationTokenSource tokenSource) { ChangeToken = changeToken; TokenSource = tokenSource; }