кэш TryGetValue против Get - PullRequest
       8

кэш TryGetValue против Get

0 голосов
/ 19 февраля 2020

Вот как я реализую свой CacheManager. Проблема, с которой я сталкиваюсь, заключается в том, что TryGetValue всегда будет возвращать значение null в RemoveFromCache функции. Эта функция вызывается после истечения срока действия одного из токенов, и поэтому я пытаюсь удалить этот токен из списка в кэше, в то время как GetAllTokens возвращает полный список всех токенов. AddTokenToCache работает правильно.

Это WebAPI на AS PNET -Core 3.0

CacheManager.cs

public class CacheManager : ICacheManager
{
    private IMemoryCache _cache;

    public CacheManager(IMemoryCache cache) {
        _cache = cache;

    }        
    public void AddTokenToCache(string appName, string tokenString)
    {
        List<Token> tokens = new List<Token>();

        //save this token against the application record in-memory
        if (!_cache.TryGetValue(CacheHelper.CacheKey_Tokens, out tokens))
        {
            if (tokens == null)
                tokens = new List<Token>();

        }

        tokens.Add(new Token
        {
            AppName = appName,
            GeneratedAt = DateTime.Now,
            TokenId = tokenString
        });

        // Set cache options.
        var cacheEntryOptions = new MemoryCacheEntryOptions()
        ;//    .SetSlidingExpiration(TimeSpan.FromSeconds(180)); //3 minutes

        _cache.Set(CacheHelper.CacheKey_Tokens, tokens, cacheEntryOptions);
    }

    public List<Token> GetAllTokens()
    {
        return _cache.Get<List<Token>>(CacheHelper.CacheKey_Tokens);
    }

    public bool RemoveFromCache(string tokenId)
    {

        List<Token> tokens = new List<Token>();            

        //remove this token from memory            
        if (!_cache.TryGetValue(CacheHelper.CacheKey_Tokens, out tokens)) {
            return false;
        }
        else
        {
            if (tokens != null && tokens.Count > 0)
            {
                //_logger.LogInfo("Processing token");
                //trimming quotations from the string
                tokenId = tokenId.Substring(1, tokenId.Length - 2);
                int index = tokens.FindIndex(t => t.TokenId == tokenId);

                if (index >= 0)
                    tokens.RemoveAt(index);

                var cacheEntryOptions = new MemoryCacheEntryOptions();
                _cache.Set(CacheHelper.CacheKey_Tokens, tokens, cacheEntryOptions);

                return true;
            }                
        }

        return false;
    }
}

Моя последовательность вызова :

  • AddTokenToCache (токен успешно добавлен в кэш)
  • GetAllToken (показывает, что токен добавлен в кэш)
  • AddTokenToCache (токен успешно добавлен в кэш)
  • GetAllToken (показывает, что оба токена добавлены в кэш)
  • Событие Fire TokenExpired, которое вызывает RemoveFromCache (токены нулевые)
  • GetAllToken (показывает, что оба токена добавлены в кэш)

Startup.cs

public void ConfigureServices(IServiceCollection services)
    { 
        services.AddSingleton<ILoggerManager, LoggerManager>();

        services.AddMemoryCache();
        services.AddDbContext<GEContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        services.AddControllers();
        services.AddRazorPages();
        services.AddSingleton<ICacheManager, CacheManager>();

        RegisterHandlerforTokenExpiredEvent(services);
        //other code removed for brevity
    }
public void RegisterHandlerforTokenExpiredEvent(IServiceCollection services)
    {
        var sp = services.BuildServiceProvider();

        var jwtManager = sp.GetService<IJWTAuthenticationManager>(); //publisher            
        var cacheManager = sp.GetService<ICacheManager>(); //subscriber

        jwtManager.TokenExpired += cacheManager.OnTokenExpired;            
    }

1 Ответ

1 голос
/ 20 февраля 2020

Это потому, что вы построили еще один ServiceProvider по services.BuildServiceProvider():

public void RegisterHandlerforTokenExpiredEvent(IServiceCollection services)

{
    var sp = services.BuildServiceProvider();  // this is a different service provider from the default one built by ASP.NET Core itself.

    var jwtManager = sp.GetService<IJWTAuthenticationManager>(); //publisher            
    var cacheManager = sp.GetService<ICacheManager>(); //subscriber
    // it doesn't work because the cacheManager is not the same instance that you use in the controllers
    jwtManager.TokenExpired += cacheManager.OnTokenExpired;            
}

В результате вы получите ICacheManager экземпляр НЕ тот же единственный , который вы вводите в Controllers / Other Services. Другими словами, у вас будет два разных экземпляра ICacheManager!

Как золотое правило, НЕ создайте еще одну копию ServiceProvider с помощью services.BuildServiceProvider() в коде уровня приложения, если только вы Вы уверены, что это хорошо для вас.

Как исправить

  1. Вместо создания другой копии поставщика услуг и последующего получения другого экземпляра, вы всегда должны использовать Io C вместо шаблона сервисного локатора.
  2. Кажется, что ваш JWTAuthenticationManager является одиночным, и вы хотите связать обработчик событий во время запуска. Если это так, вы можете зарегистрировать HostedService.

    public class MyHostedService : IHostedService
    {
        private readonly IJWTAuthenticationManager _jWTAuthManager;
        private readonly ICacheManager _cacheManager;
    
        // suppose your IJWTAuthenticationManager  is a singleton service
        public MyHostedService(IJWTAuthenticationManager jWTAuthManager, ICacheManager cacheManager)
        {
            this._jWTAuthManager = jWTAuthManager;
            this._cacheManager = cacheManager;
        }
    
        public Task StartAsync(CancellationToken cancellationToken)
        {
            this._jWTAuthManager.TokenExpired += this._cacheManager.OnTokenExpired;
            return Task.CompletedTask;
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            this._jWTAuthManager.TokenExpired -= this._cacheManager.OnTokenExpired;
            return Task.CompletedTask;
        }
    }
    

    и зарегистрировать эту службу в Startup:

    services.AddHostedService<MyHostedService>();
    

Другой способ, который не Требуется HostedService и запускается во время запуска:

Получить службу и привязать событие до Host.Run():

public static void Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();
    var jwtMgr = host.Services.GetRequiredService<IJWTAuthenticationManager>();
    var cacheMgr = host.Services.GetRequiredService<ICacheManager>();
    jwtMgr.TokenExpired = cacheMgr.OnTokenExpired;
    host.Run();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...