Programing

MVC 응용 프로그램에서 데이터를 캐시하는 방법

lottogame 2020. 4. 9. 07:56
반응형

MVC 응용 프로그램에서 데이터를 캐시하는 방법


MVC 응용 프로그램의 페이지 캐싱 및 부분 페이지 캐싱에 대한 많은 정보를 읽었습니다. 그러나 데이터를 캐시하는 방법을 알고 싶습니다.

내 시나리오에서는 LINQ to Entities (엔터티 프레임 워크)를 사용합니다. GetNames (또는 메소드가 무엇이든)에 대한 첫 번째 호출에서 데이터베이스의 데이터를 가져오고 싶습니다. 캐시 된 버전을 사용하기 위해 결과를 캐시에 저장하고 두 번째 호출에서 결과를 저장하고 싶습니다.

누구나 이것이 어떻게 작동하는지, 어디에서 구현 해야하는지 (모델?) 및 작동하는지에 대한 예를 보여줄 수 있습니까?

나는 전형적인 ASP.NET 앱에서 일반적으로 매우 정적 인 데이터를 위해 이것을 보았습니다.


모델에서 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와 관련이 없으며 항상 데이터 캐싱 에이 방법을 사용했습니다.


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

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));

캐시 공급자는 캐시에 "캐시 id"라는 이름의 것이 있는지 확인하고,없는 경우 데이터를 가져 와서 캐시에 저장하기 위해 대리자 메서드를 호출합니다.

예:

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

TT의 게시물을 언급하고 있으며 다음과 같은 접근법을 제안합니다.

모델에서 System.Web dll을 참조하고 System.Web.Caching.Cache를 사용하십시오.

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 문 추가 : 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은 System.Web.Caching 네임 스페이스에서 인 메모리 캐시 구현을 제공했습니다. 이전 버전의 .NET Framework에서는 캐싱이 System.Web 네임 스페이스에서만 사용 가능하므로 ASP.NET 클래스에 대한 종속성이 필요했습니다. .NET Framework 4에서 System.Runtime.Caching 네임 스페이스에는 웹 및 웹 이외의 응용 프로그램 모두를 위해 설계된 API가 포함되어 있습니다.

더 많은 정보:


Steve Smith는 ASP.NET MVC에서 CachedRepository 패턴을 사용하는 방법을 보여주는 두 개의 훌륭한 블로그 게시물을 작성했습니다. 리포지토리 패턴을 효과적으로 사용하며 기존 코드를 변경하지 않고도 캐싱을 수행 할 수 있습니다.

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

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

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


AppFabric Caching 은 여러 서버의 물리적 메모리를 사용하여 키-값 쌍으로 데이터를 저장하는 인 메모리 캐싱 기술입니다. 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);

왜 TI

두 번째 도우미는 대부분의 데이터 키가 복합적이지 않기 때문에 특히 좋습니다. 복합 키를 자주 사용하는 경우 추가 방법을 추가 할 수 있습니다. 이런 식으로 모든 종류의 문자열 연결 또는 string.Formats를 수행하여 키를 캐시 도우미에 전달하지 마십시오. 또한 래퍼 메소드에 ID를 전달할 필요가 없기 때문에 데이터 액세스 메소드를 더 쉽게 전달할 수 있습니다. 모든 것이 매우 간결하고 대부분의 유스 케이스에 맞게 구성됩니다.


Hrvoje Hudo의 답변이 개선되었습니다. 이 구현에는 몇 가지 주요 개선 사항이 있습니다.

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

dependOn 객체를 직렬화하기 위해 Newtonsoft.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; }
}

나는 이런 식으로 그것을 사용했고 그것은 나를 위해 작동합니다. system.web.caching.cache.add에 대한 https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspx 매개 변수 정보

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;

}

이 지속적인 데이터 문제에 대해 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);

Oli의 아래 솔루션은 합의 된 접근 방식 중 하나였습니다. noms가 null 검사에서 캐시에 대해 null이 아닌 null을 참조 할 수 있기 때문에 함수가 반환하기 전에 캐시가 만료되거나 무효화 된 경우 noms가 다른 값을 참조 할 수 없다고 생각했습니다. 뭔가 빠진 느낌이 있습니다.

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

감사합니다!


ASP MVC에 내장 된 캐싱을 시도하고 사용할 수도 있습니다.

캐시하려는 컨트롤러 메소드에 다음 속성을 추가하십시오.

[OutputCache(Duration=10)]

이 경우 ActionResult는 10 초 동안 캐시됩니다.

여기에 더 많은

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

반응형