我厌倦了一次又一次地将代码写入代码以缓存数据访问层中的对象。
无论如何都要缓存c#函数结果而不需要对函数进行太多更改。
目前是否有任何框架支持此功能?
我可以通过编写自定义“c#函数属性”来存档吗?如果是这样,请给我一些积分来开始实施?
答案 0 :(得分:22)
可能性1:使用IL编织
之前提到过Postsharp。您也可以尝试MethodCache.Fody包。
可能性2:使用代理/拦截框架
示例(Ninject& Ninject.Interception):
public class CacheAttribute : InterceptAttribute
{
public override IInterceptor CreateInterceptor(IProxyRequest request)
{
return request.Context.Kernel.Get<CachingInterceptor>();
}
}
public class CachingInterceptor : IInterceptor
{
private ICache Cache { get; set; }
public CachingInterceptor(ICache cache)
{
Cache = cache;
}
public void Intercept(IInvocation invocation)
{
string className = invocation.Request.Target.GetType().FullName;
string methodName = invocation.Request.Method.Name;
object[] arguments = invocation.Request.Arguments;
StringBuilder builder = new StringBuilder(100);
builder.Append(className);
builder.Append(".");
builder.Append(methodName);
arguments.ToList().ForEach(x =>
{
builder.Append("_");
builder.Append(x);
});
string cacheKey = builder.ToString();
object retrieve = Cache.Retrieve<object>(cacheKey);
if (retrieve == null)
{
invocation.Proceed();
retrieve = invocation.ReturnValue;
Cache.Store(cacheKey, retrieve);
}
else
{
invocation.ReturnValue = retrieve;
}
}
}
然后你可以装饰这样的函数:
[Cache]
public virtual Customer GetCustomerByID(int customerID)
{
return CustomerRepository.GetCustomerByID(customerID);
}
截获的函数必须是虚函数,并且必须由Ninject内核创建类。如果您依赖性能,可以直接通过Castle.DynamicProxy代理类(Ninject.Extensions.Interception.DynamicProxy在内部使用它)。
可能性3:使用表达式包装器
您可以将函数作为表达式传递,生成包含类,方法和参数信息的缓存键,并在缓存中找不到表达式(如果未找到)。这比AOP / Proxy框架增加了更多的运行时开销,但对于简单的解决方案就足够了。
private T CacheAction<T>(Expression<Func<T>> action, [CallerMemberName] string memberName = "") where T : class
{
MethodCallExpression body = (MethodCallExpression)action.Body;
ICollection<object> parameters = new List<object>();
foreach (MemberExpression expression in body.Arguments)
{
parameters.Add(((FieldInfo)expression.Member).GetValue(((ConstantExpression)expression.Expression).Value));
}
StringBuilder builder = new StringBuilder(100);
builder.Append(GetType().FullName);
builder.Append(".");
builder.Append(memberName);
parameters.ToList().ForEach(x =>
{
builder.Append("_");
builder.Append(x);
});
string cacheKey = builder.ToString();
T retrieve = Cache.Retrieve<T>(cacheKey);
if (retrieve == null)
{
retrieve = action.Compile().Invoke();
Cache.Store(cacheKey, retrieve);
}
return retrieve;
}
public Customer GetCustomerByID(int customerID)
{
return CacheAction(() => CustomerRepository.GetCustomerByID(customerID));
}
答案 1 :(得分:6)
答案 2 :(得分:4)
如果我认为你的问题是正确的,你想要的正确术语是memoization。维基百科提供了有关此主题的更多细节。不幸的是,没有提到支持它的C#库。
答案 3 :(得分:2)
Cache Application block是微软对.NET中缓存的内置库的回答。
答案 4 :(得分:2)
Lazy存储它的值。 示例:http://msdn.microsoft.com/en-us/vstudio/bb870976
答案 5 :(得分:0)
我建议使用Spring.Net AOP。 它基本上创建了一个代理,并且可以从/向缓存重定向调用。 http://www.springframework.net/doc/reference/html/aop-quickstart.html
然后你可以得到类似的建议:
public class CachingAroundAdvice : IMethodInterceptor
{
#region Variable Declarations
private Priority priority = Priority.Normal;
#endregion
public object Invoke(IMethodInvocation invocation)
{
// declare local variables
string cacheKey = string.Empty;
object dataObject = null;
// build cache key with some algorithm
cacheKey = CreateCacheKey(invocation.Method, invocation.Arguments);
// retrieve item from cache
dataObject = CacheManager.Cache.GetData(cacheKey);
// if the dataobject is not in cache proceed to retrieve it
if (null == dataObject)
{
dataObject = invocation.Proceed();
// add item to cache
CacheManager.Cache.Add(cacheKey, dataObject, CachePriority, null, Expiration);
}
// return data object
return dataObject;
}
答案 6 :(得分:0)
我使用System.Runetime.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.AddHours(4));
}
return item;
}
public void Clear(string cacheKey)
{
MemoryCache.Default.Remove(cacheKey);
}
}
interface ICacheService
{
T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
void Clear(string cacheKey);
}
可以按以下方式使用:
var cacheProvider = new InMemoryCache();
var cachedResult = cacheProvider.GetOrSet("YourCacheKey",
() => MethodToCache());
第一次调用该方法将缓存结果,下一次调用将返回缓存的结果。
答案 7 :(得分:0)
您可以使用Dictionary
来缓存该函数。字典将键映射到值,而函数将参数映射到值。因此,从概念上讲,字典适合作为函数的缓存。这是一个简单的类:
/// <summary>
/// The lazy function map caches the results of calls to the backing function. Every time the function is called on an argument u and returns v,
/// the pair (u, v) is stored in the dictionary.
/// </summary>
class LazyFunctionMapImpl<T, U> : ILazyFunctionMap<T, U>
{
private readonly Dictionary<T, U> _backingDictionary;
private readonly Func<T, U> _backingFunction;
public LazyFunctionMapImpl(Func<T, U> backingFunction)
{
_backingDictionary = new Dictionary<T, U>();
_backingFunction = backingFunction;
}
public U this[T index]
{
get
{
if (_backingDictionary.ContainsKey(index))
{
return _backingDictionary[index];
}
U valueAtIndex = _backingFunction(index);
_backingDictionary.Add(index, valueAtIndex);
return valueAtIndex;
}
}
public void Clear()
{
_backingDictionary.Clear();
}
}
下面是几个接口:
/// <summary>
/// A function map that should lazily cache param/result pairs until clear is called.
/// </summary>
public interface ILazyFunctionMap<T, U> : IFunctionMap<T, U>
{
/// <summary>
/// Should invalidate any caches forcing the underyling function to be called afresh
/// </summary>
void Clear();
}
public interface IFunctionMap<T, U>
{
/// <summary>
/// Mapped values representing the underlying function.
/// </summary>
U this[T index] { get; }
}