Asp.Net Core In-Memory Cache

Daha önceki Asp.Net Core yazılarında as.net core'a giriş yapıp sonrasında asp.net core framework ile birlikte gelen built-in container'ını incelemiştik.

Asp.Net Core Windows, Linux, MacOS üzerinde çalışan moden web uygulamaları geliştirmemizi sağlayan modüler bir framework'dür. Modüler olmasının dezavantajı olarak da klasik Asp.net kütüphanesine kıyasla içerisinde default olarak gelen bir çok özellik bulunmamaktadır. Bunlardan biride default olarak içerisinde bir Cache object bulunmuyor ancak bir kaç küçük geliştirmeyle uygulamanıza hem in-memory hemde distributed caching özelliklerini kazandırabiliyoruz.. 

Bu yazımızda da asp.net core uygulamamıza in-memory cache özelliğini nasıl kazandırabiliriz basit bir örnek ile  inceleyeceğiz. 

Enable In-Memory Cache

In-memory cache özelliği asp.net core içerisinde bir service olarak bulunmaktadır. Bu servis default kapalı gelir yapmamız gereken startup.cs içerisinde bulunan ConfigureServices metodunda aşağıdaki gibi cache servisini açmak.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddMemoryCache();
}

Core projelerinde in-memory cache kullanmamızı sağlayan arayüzün adı IMemoryCache. Bu interface'e ait metotları vs. kullanarak cache set,get,remove gibi işlemleri yapabiliriz.

public interface IMemoryCache : IDisposable
{
    bool TryGetValue(object key, out object value);
    ICacheEntry CreateEntry(object key);
    void Remove(object key);
}

Using IMemoryCache to Cache

ConfigureServices metodu içerisinde servisi aktifleştirdikten sonra IMemoryCache interface'ini kullanmak istediğimiz katmana ait constructor'da inject etmemiz gerekmekte.
Bizde geriye product list return ettiğimiz bir controller tanımlayarak IMemoryCache interface'ini aşağıdaki gibi const. inj. parameter olarak verelim.

[Route("api/[controller]")]
public class ProductController : Controller
{
    private readonly IMemoryCache _memoryCache;
    public ProductController(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    // GET api/values
    [HttpGet]
    public IEnumerable<Product> Get()
    {

    }
}


public class Product
{
    public int Quantity { get; set; }
    public string Name { get; set; }
}

Şimdi ise get metodunun içerisini dolduralım. Set metodu parametre olarak 1:key, 2:value, 3:cacheOptions . Cache options olarak AbsoluteExpiration;cache expire süresi ve Priority; memory şiştiğinde cache objelerini hangi priority'de silecek bunun bilgisinin bulunduğu ayarları set edeceğiz. 

[HttpGet]
public IEnumerable<Product> Get()
{
    const string cacheKey = "productListKey";

    if (!_memoryCache.TryGetValue(cacheKey, out List<Product> response))
    {
        response = new List<Product> { new Product { Name = "test 1 ", Quantity = 20 }, new Product { Name = "test 2", Quantity = 45 } };

        var cacheExpirationOptions =
            new MemoryCacheEntryOptions
            {
                AbsoluteExpiration = DateTime.Now.AddMinutes(30),
                Priority = CacheItemPriority.Normal
            };
        _memoryCache.Set(cacheKey, response, cacheExpirationOptions);
    }
    return response;
}

Gelen ilk request için cache'de o key'e ait bir obje olmadığından ilk response source'a gidip(bir repository yada service layer olabilir) dönen değer alınıp 30 dkka expire süresi set edilerek oluşturacaktır. Artık ondan sonraki bütün request'ler 30 dkka süresince source'a gitmeden response'u cache'de bulup Get işlemi yapıp return edecektir. Expire süresi dolduğunda ise ilgili key ve obje cache'den silinecektir.

Set, Get yapabildiğimiz gibi Remove işlemide yapabiliriz. Bunun için cacheKey değerini parametre olarak Remove metoduna verip call yapmak yeterli.

 _memoryCache.Remove(cacheKey);

CacheItemPriority enum'ı içerisinde Low, Normal, High, NeverRemove değerleri mevcut. CachedObject Priority değerine göre memory de yer açmak için sırayla silinir. Memory'den otomatik silme işlemi yapıldığında bunun bilgisini bize iletmesini sağlayan bir callback handler metodunu aşağıdaki gibi options'a register edebiliriz ve silme işlemi yapılırken bu metot tetiklenerek bize haber verir.

 cacheExpirationOptions.RegisterPostEvictionCallback
     (ProductGetALLCacheItemChangedHandler, this);
 _memoryCache.Set(cacheKey, response, cacheExpirationOptions);

Cache nerede ve nasıl uygulanması gerektiğine karar verildiğinde server-side bir uygulama için olmazsa özelliklerden biri haline gelmiştir. Asp.net core'da da yazının başında bahsettiğimiz gibi memory ve distributed cache işlemleri yapmamızı sağlayan service'ler bulunmaktadır. Bu yazımızda basitçe memory cache özelliğini projemize nasıl kazandırabiliriz konusuna değindik. Sonraki yazılarda redis kullanarak distributed cache yapısını uygulamamıza nasıl entegre edebiliriz inceleyeceğiz.

Comments (9) -

  • Merhabalar, yazınız çok güzel öncelikle

    Bir sorum olucak "memorycache" controller seviyesinde çalışıyor ama .net core web projesine referans ettiğim library project içinde çalışmıyor  kodlar ben mi birşeyi yanlış yapıyorum yoksa normal mi ?

    .net core 2.0
    • Merhaba, değerli yorumun için teşekkürler.
      Eğer web projesinde yazıda belirtilen cache module'ünü enable ettiysen bu projeye referans verilen bütün .net core library'lerin de IMemoryCache interface'ini kullanarak cache yönetimini rahatlıkla yapabiliyor olmalısın. İlgili kod bloğunu mail olarak iletebirsen detaylıca bakabiliriz.
  • Merhaba,
    redis-server.exe açıkken  kodlar çalışıyor ama kaplıyken çalışmıyor. Redisi otomatik olarak çalıştıramıyor muyuz ?
    • Merhaba
      Redis'i windows makinanda service olarak tanımlayabilirsin yada bir docker-container üzerinde host edip container ayakta olduğu sürece redis-server da çalışacaktır.
  • Request'e göre yapabiliyor muyuz? Yani kullanıcı bazlı değişken verilerde kullanılabiliyor mu? Daha net anlatmak gerekirse, ürün listesi çektiğimizi varsayalım, ancak bir ürünü beli bir kullanıcı için kırmızı kazaklarda %20, mavi kazaklarda %40 indirim yapmak istiyorum diyelim. Bunu cache te tutup nasıl döndürebilirim?
    • Yapabilirsin tabiki. İlgili kullanıcının indirimleri hesaplanmış ürün listesini ilk defa Cache atarken CustomerId yada tam olarak karşılığı neyse bu bilgiyi de cacheKey hesaplarken kullanabilir yada Cache'de bulunan bilgileri retriew ederken customer bazlı filtreleyebileceğin bir yapı tasarlayarak kullanabilirsin diye düşünüyorum.
  • Emeğin için teşekkür ederim. Klavyene sağlık.
    Bu sistem memory üzerinde çalıştığı için dikkat etmemiz gereken noktalar nedir?
    • Değerli yorumun için teşekkürler, huge-amount-of-data dediğimiz oldukça büyük mb'daki verileri in-memory cache'de saklamaman uygulamanın çalıştığı makina için iyi olur. Birde bu distributed sistemler için önerilen bir yöntem değil diyebiliriz çünkü cache datasıno o anki uygulamanın host edildiği makinanın ram'inde saklıyorsun. Distributed sistemler için Redis vs düşünebilirsin.

Add comment