0

I have encountered a strange thing. I have a web api project of .net 6. When IServiceProvider starts, it will use FileService to read data and store it in the cache, and set IChangeToken. If the source file changes, the contents of the cache will be reset. I have six classes map from different tables of the same sqlite file, and then there are six different cache keys. However, for some reason, only three of them are being triggered. When I set a breakpoint in the controller, the number of cache keys still shows six. I can't find the reason why this problem happens.

Log info:

2023-07-24 16:12:11.8955|INFO|Weather.FileService|============ Service.SetCache
2023-07-24 16:12:12.8782|INFO|Weather.FileService|set cachekey: Rain
2023-07-24 16:12:13.5386|INFO|Weather.FileService|set cachekey: Humi
2023-07-24 16:12:14.0758|INFO|Weather.FileService|set cachekey: Temp
2023-07-24 16:12:15.0917|INFO|Weather.FileService|set cachekey: Wind
2023-07-24 16:12:15.0917|INFO|Weather.FileService|set cachekey: WeatherInfo
2023-07-24 16:12:15.0979|INFO|Weather.FileService|set cachekey: DataSlice
2023-07-24 16:13:06.6945|INFO|Weather.FileService|============= CacheExpireHandler trigger, key: WeatherInfo
2023-07-24 16:13:08.1085|INFO|Weather.FileService|============= CacheExpireHandler trigger, key: DataSlice
2023-07-24 16:13:09.3240|INFO|Weather.FileService|============= CacheExpireHandler trigger, key: Wind
2023-07-24 16:13:09.3240|INFO|Weather.FileService|set cachekey: DataSlice
2023-07-24 16:13:09.3240|INFO|Weather.FileService|set cachekey: WeatherInfo
2023-07-24 16:13:16.5196|INFO|Weather.FileService|set cachekey: Wind

HostedService:

public void SetCache()
{
    _logger.LogInformation("============ Service.SetCache");
    using (var conn = SQLiteDatabase.OpenConnection(DataFilePath))
    {
        _fileService.SetCache("Rain", conn.GetAll<Rain>());
        _fileService.SetCache("Humi", conn.GetAll<Humi>());
        _fileService.SetCache("Temp", conn.GetAll<Temp>());
        _fileService.SetCache("Wind", conn.GetAll<Wind>());
        _fileService.SetCache("WeatherInfo", conn.GetAll<WeatherInfo>());
        _fileService.SetCache("DataSlice", conn.GetAll<DataSlice>());
    }
}

public Task StartAsync(CancellationToken cancellationToken)
{
    try
    {
        SetCache();
    }
    catch (Exception e)
    {
        _logger.LogError(e.ToString());
    }

    return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
    return Task.CompletedTask;
}

FileService:

public void SetCache<T>(string cacheKey, IEnumerable<T> list)
{
    _logger.LogInformation("========= set cachekey: " + cacheKey);
    var fileProvider = new PhysicalFileProvider(Service.DataFolderPath);
    IChangeToken changeToken = fileProvider.Watch(Service.DataFileName);
    MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
        .AddExpirationToken(changeToken)
        .RegisterPostEvictionCallback(CacheExpireHandler);

    _cache.Set(cacheKey, list, cacheEntryOptions);

    if (cacheKey == nameof(DataSlice))
    {
        PreviousDataSlice = (IEnumerable<DataSlice>)list;
    }
}

private void CacheExpireHandler(object key, object oldValue, EvictionReason reason, object state)
{
    _logger.LogInformation("============= CacheExpireHandler trigger, key: " + key);
    if (reason == EvictionReason.TokenExpired && key != null)
    {
        using var conn = SQLiteDatabase.OpenConnection(Service.DataFilePath);
        string cacheKey = key.ToString() ?? string.Empty;

        switch (cacheKey)
        {
            case "Rain":
                SetCache(cacheKey, conn.GetAll<Rain>());
            break;

            case "Temp":
                SetCache(cacheKey, conn.GetAll<Temp>());
                break;

            case "Humi":
                SetCache(cacheKey, conn.GetAll<Humi>());
                break;

            case "Wind":
                SetCache(cacheKey, conn.GetAll<Wind>());
                break;

            case "DataSlice":
                SetCache(cacheKey, conn.GetAll<DataSlice>());
                break;

            case "WeatherInfo":
                SetCache(cacheKey, conn.GetAll<WeatherInfo>());
                break;

            default:
                _logger.LogInformation("=========== ?????  key:" + cacheKey);
                break;
        }
    }
    else
    {
        _logger.LogWarning($"key: {key}, oldValue:{oldValue}, reason: {reason} ,state: {state}");
    }
}

Program.cs:

try
{
    var builder = WebApplication.CreateBuilder(args);

    // NLog: Setup NLog for Dependency injection
    builder.Logging.ClearProviders();
    builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information);
    builder.Host.UseNLog();

    // Add services to the container.
    builder.Services.AddMemoryCache();
    builder.Services.AddHostedService<HostedService>();
    builder.Services.AddSingleton<FileService>();     
    builder.Services.AddControllers();

    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    builder.Services.AddControllers().AddNewtonsoftJson();

    var app = builder.Build();    

    // Configure the HTTP request pipeline.
    //if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }

    app.UseAuthorization();

    app.UseExceptionHandler("/error");

    app.MapControllers();

    NLog.LogManager.Setup().LoadConfigurationFromAppSettings();
    app.UseElmah();

    app.Run();

}
catch (Exception e)
{
    logger.Error(e, "Stopped program because of exception");
    throw;
}
finally
{
    // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
    NLog.LogManager.Shutdown();
}
Ching Wang
  • 49
  • 8
  • Have you hit a breakpoint in the `switch...case`? Can it be matched and called normally? – Chen Jul 25 '23 at 09:20
  • @Chen I hit a breakpoint in the switch...case and it not matched, but I don't know why, like above my code each cache key is set in the same way. – Ching Wang Jul 26 '23 at 03:19
  • And there is also a strange thing that every time the trigger is successful, the number of triggered events will be reduced, but it will not decrease after a certain number. – Ching Wang Jul 26 '23 at 03:19
  • 1
    Do you mean that the value of `cacheKey` is `Rain`, but you cannot implement `SetCache(cacheKey, conn.GetAll());`? Have you tried testing without using the table to map? It would be best if you could provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Chen Jul 26 '23 at 09:08
  • When the `HostedService` started, the `HostedService` successfully set the `Rain` data into the cache key, but it did not trigger the `NcdrCacheExpireHandler` when the file changed, but other types of data did. – Ching Wang Jul 28 '23 at 03:03
  • I tried to without using the table to map and it's works! But I can't understand why this is happening? I use the `Dommel` as an ORM, and I have tried to convert it into a `List`, but it doesn’t work. It seems that I can only find other ways to avoid this problem. Thank you for your help. – Ching Wang Jul 28 '23 at 03:40

0 Answers0