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가 포함되어 있습니다.
더 많은 정보:
https://msdn.microsoft.com/en-us/library/dd997357(v=vs.110).aspx
https://docs.microsoft.com/en-us/dotnet/framework/performance/caching-in-net-framework-applications
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
'Programing' 카테고리의 다른 글
'svn cleanup'이 실패하면 어떻게해야합니까? (0) | 2020.04.09 |
---|---|
삭제 후 SQL Server에서 자동 증분 재설정 (0) | 2020.04.09 |
C #의 간단한 상태 머신 예제? (0) | 2020.04.09 |
컴파일 경고 : 아키텍처 i386의 파일을 처리 할 규칙이 없습니다. (0) | 2020.04.09 |
웹 브라우저가 https를 통해 콘텐츠를 캐시합니까? (0) | 2020.04.08 |