source

MVC 어플리케이션에서 데이터를 캐시하는 방법

manycodes 2023. 10. 3. 11:06
반응형

MVC 어플리케이션에서 데이터를 캐시하는 방법

저는 MVC 어플리케이션에서 페이지 캐싱과 부분 페이지 캐싱에 대한 많은 정보를 읽었습니다.하지만 데이터를 어떻게 캐싱할 것인지 알고 싶습니다.

시나리오에서는 LINQ to Entities(엔티 프레임워크)를 사용할 예정입니다.GetNames(또는 메서드가 무엇이든 간에)에 처음 호출할 때 데이터베이스에서 데이터를 가져오려고 합니다.결과를 캐시에 저장하고 캐시된 버전이 존재하는 경우 두 번째 호출 시에 캐시된 버전을 사용하고자 합니다.

이것이 어떻게 작동하는지, 이것이 어디에서 구현되어야 하는지(모델), 그리고 작동하는지에 대한 예시를 보여줄 수 있는 사람이 있습니까?

저는 이것을 전통적인 ASP에서 본 적이 있습니다.일반적으로 매우 정적인 데이터를 위한 NET 앱.

제가 사용하는 멋지고 간단한 캐시 도우미 클래스/서비스는 다음과 같습니다.

using System.Runtime.Caching;  

public class InMemoryCache: ICacheService
{
    public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
    {
        T item = MemoryCache.Default.Get(cacheKey) as T;
        if (item == null)
        {
            item = getItemCallback();
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(10));
        }
        return item;
    }
}

interface ICacheService
{
    T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}

용도:

cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));

캐시 공급자는 캐시에 "cache id"라는 이름의 것이 있는지 확인하고, 없으면 위임자 방식을 호출하여 데이터를 가져와 캐시에 저장합니다.

예:

var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())

를 합니다.System.Web에서 dll합니다를 합니다.System.Web.Caching.Cache

    public string[] GetNames()
    {
      string[] names = Cache["names"] as string[];
      if(names == null) //not in cache
      {
        names = DB.GetNames();
        Cache["names"] = names;
      }
      return names;
    }

조금 단순하지만 그래도 될 것 같네요.이것은 MVC에 특정한 것이 아니며 저는 항상 데이터 캐싱을 위해 이 방법을 사용해 왔습니다.

TT의 게시물을 참고하여 다음과 같은 방법을 제안합니다.

시스템을 참조합니다.모델에서 웹 dll을 사용하고 시스템을 사용합니다.웹.캐시.캐시

public string[] GetNames()
{ 
    var noms = Cache["names"];
    if(noms == null) 
    {    
        noms = DB.GetNames();
        Cache["names"] = noms; 
    }

    return ((string[])noms);
}

캐시에서 다시 읽은 값을 반환해서는 안 됩니다. 특정 시점에 캐시가 아직 있는지 여부를 알 수 없기 때문입니다.이전에 문에 삽입했더라도 이미 사라졌거나 캐시에 추가되지 않았을 수 있습니다.

따라서 데이터베이스에서 읽은 데이터를 추가하고 캐시에서 다시 읽지 않고 직접 반환합니다.

.NET 4.5+ 프레임워크의 경우

:System.Runtime.Caching

을 사용하여 :using System.Runtime.Caching;

public string[] GetNames()
{ 
    var noms = System.Runtime.Caching.MemoryCache.Default["names"];
    if(noms == null) 
    {    
        noms = DB.GetNames();
        System.Runtime.Caching.MemoryCache.Default["names"] = noms; 
    }

    return ((string[])noms);
}

에서.NET Framework 3.5 이전 버전, ASP.NET은 시스템에서 인메모리 캐시 구현을 제공했습니다.웹.캐시 네임스페이스.의 이전 버전에서.NET Framework, 캐싱은 시스템에서만 가능했습니다.웹 네임스페이스이므로 ASP에 대한 종속성이 필요합니다.NET 클래스.에서.NET Framework 4, 시스템.런타임.캐싱 네임스페이스에는 웹 애플리케이션과 비웹 애플리케이션 모두를 위해 설계된 API가 포함되어 있습니다.

자세한 정보:

Steve Smith는 ASP에서 CashedRepository 패턴을 사용하는 방법을 보여주는 두 개의 훌륭한 블로그 게시물을 작성했습니다.NET MVC.저장소 패턴을 효과적으로 사용하며 기존 코드를 변경할 필요 없이 캐싱할 수 있습니다.

http://ardalis.com/Introducing-the-CachedRepository-Pattern

http://ardalis.com/building-a-cachedrepository-via-strategy-pattern

이 두 개의 게시물에서 그는 이 패턴을 설정하는 방법을 보여주고 또한 왜 그것이 유용한지를 설명합니다.이 패턴을 사용하면 기존 코드가 캐싱 로직을 보지 않고 캐싱할 수 있습니다.기본적으로 캐시된 저장소를 마치 다른 저장소처럼 사용합니다.

저는 이런 식으로 사용해 보았는데 저한테 효과가 있어요.https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspx 시스템에 대한 매개 변수 정보입니다.웹.caching.cache.추가.

public string GetInfo()
{
     string name = string.Empty;
     if(System.Web.HttpContext.Current.Cache["KeyName"] == null)
     {
         name = GetNameMethod();
         System.Web.HttpContext.Current.Cache.Add("KeyName", name, null, DateTime.Noew.AddMinutes(5), Cache.NoSlidingExpiration, CacheitemPriority.AboveNormal, null);
     }
     else
     {
         name = System.Web.HttpContext.Current.Cache["KeyName"] as string;
     }

      return name;

}

AppFabric 캐싱은 여러 서버에 걸쳐 물리적 메모리를 사용하여 키 값 쌍으로 데이터를 저장하는 메모리 내 캐싱 기술이며 분산되어 있습니다.AppFabric은 에 대한 성능 및 확장성 향상을 제공합니다.NET Framework 애플리케이션.개념과 건축

@Hrvoje Hudo의 대답을 확장하면...

코드:

using System;
using System.Runtime.Caching;

public class InMemoryCache : ICacheService
{
    public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class
    {
        TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
        if (item == null)
        {
            item = getItemCallback();
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
        }
        return item;
    }

    public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class
    {
        string cacheKey = string.Format(cacheKeyFormat, id);
        TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
        if (item == null)
        {
            item = getItemCallback(id);
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
        }
        return item;
    }
}

interface ICacheService
{
    TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class;
    TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class;
}

단일 항목 캐싱(항목 유형에 대한 전체 카탈로그를 캐싱하는 것이 너무 많기 때문에 각 항목이 ID를 기반으로 캐싱되는 경우).

Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);

모든 것을 캐싱하는 것.

IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);

왜 TID

두 번째 도우미는 대부분의 데이터 키가 복합적이지 않기 때문에 특히 좋습니다.복합 키를 자주 사용하는 경우 추가 방법이 추가될 수 있습니다.이렇게 하면 모든 종류의 문자열 연결이나 문자열을 사용하지 않게 됩니다.캐시 도우미에게 전달할 키를 가져오는 형식입니다.또한 ID를 래퍼 방식으로 전달할 필요가 없기 때문에 데이터 접근 방식을 쉽게 전달할 수 있습니다.대부분의 사용 사례에 대해 전체적으로 매우 간결해지고 일관성을 유지할 수 있습니다.

여기 Hrvoje Hudo의 답변에 대한 개선점이 있습니다.이 구현에는 다음과 같은 몇 가지 주요 개선 사항이는 다음과 같습니다.

  • 캐시 키는 데이터를 업데이트하는 기능과 종속성을 지정하는 전달된 개체를 기반으로 자동으로 생성됩니다.
  • 모든 캐시 기간에 대한 시간 범위 전달
  • 나사산 안전을 위해 잠금 장치 사용

이것은 Newtonsoft에 의존합니다.dependOn 개체를 직렬화하는 Json이지만 다른 직렬화 방법으로 쉽게 바꿀 수 있습니다.

ICache.cs

public interface ICache
{
    T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class;
}

InMemoryCache.cs

using System;
using System.Reflection;
using System.Runtime.Caching;
using Newtonsoft.Json;

public class InMemoryCache : ICache
{
    private static readonly object CacheLockObject = new object();

    public T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class
    {
        string cacheKey = GetCacheKey(getItemCallback, dependsOn);
        T item = MemoryCache.Default.Get(cacheKey) as T;
        if (item == null)
        {
            lock (CacheLockObject)
            {
                item = getItemCallback();
                MemoryCache.Default.Add(cacheKey, item, DateTime.Now.Add(duration));
            }
        }
        return item;
    }

    private string GetCacheKey<T>(Func<T> itemCallback, object dependsOn) where T: class
    {
        var serializedDependants = JsonConvert.SerializeObject(dependsOn);
        var methodType = itemCallback.GetType();
        return methodType.FullName + serializedDependants;
    }
}

용도:

var order = _cache.GetOrSet(
    () => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
    , new { id = orderId }
    , new TimeSpan(0, 10, 0)
);
public sealed class CacheManager
{
    private static volatile CacheManager instance;
    private static object syncRoot = new Object();
    private ObjectCache cache = null;
    private CacheItemPolicy defaultCacheItemPolicy = null;

    private CacheEntryRemovedCallback callback = null;
    private bool allowCache = true;

    private CacheManager()
    {
        cache = MemoryCache.Default;
        callback = new CacheEntryRemovedCallback(this.CachedItemRemovedCallback);

        defaultCacheItemPolicy = new CacheItemPolicy();
        defaultCacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0);
        defaultCacheItemPolicy.RemovedCallback = callback;
        allowCache = StringUtils.Str2Bool(ConfigurationManager.AppSettings["AllowCache"]); ;
    }
    public static CacheManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new CacheManager();
                    }
                }
            }

            return instance;
        }
    }

    public IEnumerable GetCache(String Key)
    {
        if (Key == null || !allowCache)
        {
            return null;
        }

        try
        {
            String Key_ = Key;
            if (cache.Contains(Key_))
            {
                return (IEnumerable)cache.Get(Key_);
            }
            else
            {
                return null;
            }
        }
        catch (Exception)
        {
            return null;
        }
    }

    public void ClearCache(string key)
    {
        AddCache(key, null);
    }

    public bool AddCache(String Key, IEnumerable data, CacheItemPolicy cacheItemPolicy = null)
    {
        if (!allowCache) return true;
        try
        {
            if (Key == null)
            {
                return false;
            }

            if (cacheItemPolicy == null)
            {
                cacheItemPolicy = defaultCacheItemPolicy;
            }

            String Key_ = Key;

            lock (Key_)
            {
                return cache.Add(Key_, data, cacheItemPolicy);
            }
        }
        catch (Exception)
        {
            return false;
        }
    }

    private void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
    {
        String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString());
        LogManager.Instance.Info(strLog);
    }
}

저는 두 개의 클래스를 사용합니다.먼저 캐시 코어 개체:

public class Cacher<TValue>
    where TValue : class
{
    #region Properties
    private Func<TValue> _init;
    public string Key { get; private set; }
    public TValue Value
    {
        get
        {
            var item = HttpRuntime.Cache.Get(Key) as TValue;
            if (item == null)
            {
                item = _init();
                HttpContext.Current.Cache.Insert(Key, item);
            }
            return item;
        }
    }
    #endregion

    #region Constructor
    public Cacher(string key, Func<TValue> init)
    {
        Key = key;
        _init = init;
    }
    #endregion

    #region Methods
    public void Refresh()
    {
        HttpRuntime.Cache.Remove(Key);
    }
    #endregion
}

두 번째는 캐시 개체 목록입니다.

public static class Caches
{
    static Caches()
    {
        Languages = new Cacher<IEnumerable<Language>>("Languages", () =>
                                                          {
                                                              using (var context = new WordsContext())
                                                              {
                                                                  return context.Languages.ToList();
                                                              }
                                                          });
    }
    public static Cacher<IEnumerable<Language>> Languages { get; private set; }
}

이 지속적인 데이터 문제에 대해 Singleton을 구현하는 것이 이전 솔루션이 매우 복잡하다고 판단되는 경우 이 문제에 대한 해결책이 될 수 있다고 말씀드리겠습니다.

 public class GPDataDictionary
{
    private Dictionary<string, object> configDictionary = new Dictionary<string, object>();

    /// <summary>
    /// Configuration values dictionary
    /// </summary>
    public Dictionary<string, object> ConfigDictionary
    {
        get { return configDictionary; }
    }

    private static GPDataDictionary instance;
    public static GPDataDictionary Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new GPDataDictionary();
            }
            return instance;
        }
    }

    // private constructor
    private GPDataDictionary() { }

}  // singleton
HttpContext.Current.Cache.Insert("subjectlist", subjectlist);

ASP MVC에 내장된 캐싱을 사용해 볼 수도 있습니다.

캐시할 컨트롤러 메서드에 다음 속성을 추가합니다.

[OutputCache(Duration=10)]

이 경우 이 작업의 Action Result는 10초 동안 캐시됩니다.

자세한 내용은 여기

언급URL : https://stackoverflow.com/questions/343899/how-to-cache-data-in-a-mvc-application

반응형