我如何cachingASP.NET MVC中的对象?

我想cachingASP.NET MVC中的对象。 我有一个BaseController ,我想要所有的控制器inheritance。 在BaseController中有一个User属性,它将简单地从数据库中获取用户数据,以便我可以在控制器中使用它,或者将它传递给视图。

我想caching这些信息。 我在每个页面上使用这些信息,所以不需要每个页面请求都去数据库。

我想要这样的东西:

 if(_user is null) GrabFromDatabase StuffIntoCache return CachedObject as User 

如何在ASP.NET MVC中实现简单的caching?

您仍然可以使用caching(在所有响应之间共享)和会话(每个用户唯一)进行存储。

我喜欢下面的“尝试从caching/创build和存储”模式(类似c#的伪代码):

 public static class CacheExtensions { public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator) { var result = cache[key]; if(result == null) { result = generator(); cache[key] = result; } return (T)result; } } 

你会这样使用它:

 var user = HttpRuntime .Cache .GetOrStore<User>( $"User{_userId}", () => Repository.GetUser(_userId)); 

您可以将此模式调整为Session,ViewState(ugh)或任何其他caching机制。 你也可以扩展ControllerContext.HttpContext(我认为它是System.Web.Extensions中的包装器之一),或者创build一个新类来做一些空间来模拟caching。

我拿了Will的回答,修改它以使CacheExtensions类是静态的,并且为了处理Func<T>null的可能性,

 public static class CacheExtensions { private static object sync = new object(); public const int DefaultCacheExpiration = 20; /// <summary> /// Allows Caching of typed data /// </summary> /// <example><![CDATA[ /// var user = HttpRuntime /// .Cache /// .GetOrStore<User>( /// string.Format("User{0}", _userId), /// () => Repository.GetUser(_userId)); /// /// ]]></example> /// <typeparam name="T"></typeparam> /// <param name="cache">calling object</param> /// <param name="key">Cache key</param> /// <param name="generator">Func that returns the object to store in cache</param> /// <returns></returns> /// <remarks>Uses a default cache expiration period as defined in <see cref="CacheExtensions.DefaultCacheExpiration"/></remarks> public static T GetOrStore<T>( this Cache cache, string key, Func<T> generator ) { return cache.GetOrStore( key, (cache[key] == null && generator != null) ? generator() : default( T ), DefaultCacheExpiration ); } /// <summary> /// Allows Caching of typed data /// </summary> /// <example><![CDATA[ /// var user = HttpRuntime /// .Cache /// .GetOrStore<User>( /// string.Format("User{0}", _userId), /// () => Repository.GetUser(_userId)); /// /// ]]></example> /// <typeparam name="T"></typeparam> /// <param name="cache">calling object</param> /// <param name="key">Cache key</param> /// <param name="generator">Func that returns the object to store in cache</param> /// <param name="expireInMinutes">Time to expire cache in minutes</param> /// <returns></returns> public static T GetOrStore<T>( this Cache cache, string key, Func<T> generator, double expireInMinutes ) { return cache.GetOrStore( key, (cache[key] == null && generator != null) ? generator() : default( T ), expireInMinutes ); } /// <summary> /// Allows Caching of typed data /// </summary> /// <example><![CDATA[ /// var user = HttpRuntime /// .Cache /// .GetOrStore<User>( /// string.Format("User{0}", _userId),_userId)); /// /// ]]></example> /// <typeparam name="T"></typeparam> /// <param name="cache">calling object</param> /// <param name="key">Cache key</param> /// <param name="obj">Object to store in cache</param> /// <returns></returns> /// <remarks>Uses a default cache expiration period as defined in <see cref="CacheExtensions.DefaultCacheExpiration"/></remarks> public static T GetOrStore<T>( this Cache cache, string key, T obj ) { return cache.GetOrStore( key, obj, DefaultCacheExpiration ); } /// <summary> /// Allows Caching of typed data /// </summary> /// <example><![CDATA[ /// var user = HttpRuntime /// .Cache /// .GetOrStore<User>( /// string.Format("User{0}", _userId), /// () => Repository.GetUser(_userId)); /// /// ]]></example> /// <typeparam name="T"></typeparam> /// <param name="cache">calling object</param> /// <param name="key">Cache key</param> /// <param name="obj">Object to store in cache</param> /// <param name="expireInMinutes">Time to expire cache in minutes</param> /// <returns></returns> public static T GetOrStore<T>( this Cache cache, string key, T obj, double expireInMinutes ) { var result = cache[key]; if ( result == null ) { lock ( sync ) { result = cache[key]; if ( result == null ) { result = obj != null ? obj : default( T ); cache.Insert( key, result, null, DateTime.Now.AddMinutes( expireInMinutes ), Cache.NoSlidingExpiration ); } } } return (T)result; } } 

我还会考虑进一步实现可扩展System.Web.HttpSessionStateBase抽象类的可testingSession解决scheme。

 public static class SessionExtension { /// <summary> /// /// </summary> /// <example><![CDATA[ /// var user = HttpContext /// .Session /// .GetOrStore<User>( /// string.Format("User{0}", _userId), /// () => Repository.GetUser(_userId)); /// /// ]]></example> /// <typeparam name="T"></typeparam> /// <param name="cache"></param> /// <param name="key"></param> /// <param name="generator"></param> /// <returns></returns> public static T GetOrStore<T>( this HttpSessionStateBase session, string name, Func<T> generator ) { var result = session[name]; if ( result != null ) return (T)result; result = generator != null ? generator() : default( T ); session.Add( name, result ); return (T)result; } } 

如果你想caching请求的长度,把它放在你的控制器基类中:

 public User User { get { User _user = ControllerContext.HttpContext.Items["user"] as User; if (_user == null) { _user = _repository.Get<User>(id); ControllerContext.HttpContext.Items["user"] = _user; } return _user; } } 

如果要caching更长时间,请使用将ControllerContext调用replace为Cache []。 如果您select使用Cache对象caching更长时间,则需要使用唯一的caching键,因为它将在请求/用户之间共享。

我喜欢隐藏数据caching在存储库中的事实。 您可以通过HttpContext.Current.Cache属性访问caching,并使用“User”+ id.ToString()作为键来存储用户信息。

这意味着从存储库中对用户数据的所有访问将使用caching的数据(如果可用),并且不需要在模型,控制器或视图中更改代码。

我已经使用此方法来纠正查询每个用户属性的数据库的系统上的严重的性能问题,并减less了从几分钟到一位数秒的页面加载时间。

@njappboy:很好的实现。 我只会推迟Generator( )调用,直到最后一个负责任的时刻。 因此您也可以caching方法调用。

 /// <summary> /// Allows Caching of typed data /// </summary> /// <example><![CDATA[ /// var user = HttpRuntime /// .Cache /// .GetOrStore<User>( /// string.Format("User{0}", _userId), /// () => Repository.GetUser(_userId)); /// /// ]]></example> /// <typeparam name="T"></typeparam> /// <param name="Cache">calling object</param> /// <param name="Key">Cache key</param> /// <param name="Generator">Func that returns the object to store in cache</param> /// <returns></returns> /// <remarks>Uses a default cache expiration period as defined in <see cref="CacheExtensions.DefaultCacheExpiration"/></remarks> public static T GetOrStore<T>( this Cache Cache, string Key, Func<T> Generator ) { return Cache.GetOrStore( Key, Generator, DefaultCacheExpiration ); } /// <summary> /// Allows Caching of typed data /// </summary> /// <example><![CDATA[ /// var user = HttpRuntime /// .Cache /// .GetOrStore<User>( /// string.Format("User{0}", _userId), /// () => Repository.GetUser(_userId)); /// /// ]]></example> /// <typeparam name="T"></typeparam> /// <param name="Cache">calling object</param> /// <param name="Key">Cache key</param> /// <param name="Generator">Func that returns the object to store in cache</param> /// <param name="ExpireInMinutes">Time to expire cache in minutes</param> /// <returns></returns> public static T GetOrStore<T>( this Cache Cache, string Key, Func<T> Generator, double ExpireInMinutes ) { var Result = Cache [ Key ]; if( Result == null ) { lock( Sync ) { if( Result == null ) { Result = Generator( ); Cache.Insert( Key, Result, null, DateTime.Now.AddMinutes( ExpireInMinutes ), Cache.NoSlidingExpiration ); } } } return ( T ) Result; } 

如果您不需要ASP.NETcaching的特定失效function,那么静态字段非常好,重量轻,易于使用。 但是,只要您需要高级function,就可以切换到ASP.NET的Cache对象进行存储。

我使用的方法是创build一个属性和一个private领域。 如果该字段为null ,则该属性将填充并返回。 我还提供了一个InvalidateCache方法,手动将该字段设置为null 。 这种方法的好处是,caching机制被封装在属性中,如果你愿意,你可以切换到不同的方法。

用最小的cachinglocking实现。 存储在caching中的值被包装在一个容器中。 如果该值不在caching中,则值容器被locking。 caching仅在创build容器时被locking。

 public static class CacheExtensions { private static object sync = new object(); private class Container<T> { public T Value; } public static TValue GetOrStore<TValue>(this Cache cache, string key, Func<TValue> create, TimeSpan slidingExpiration) { return cache.GetOrStore(key, create, Cache.NoAbsoluteExpiration, slidingExpiration); } public static TValue GetOrStore<TValue>(this Cache cache, string key, Func<TValue> create, DateTime absoluteExpiration) { return cache.GetOrStore(key, create, absoluteExpiration, Cache.NoSlidingExpiration); } public static TValue GetOrStore<TValue>(this Cache cache, string key, Func<TValue> create, DateTime absoluteExpiration, TimeSpan slidingExpiration) { return cache.GetOrCreate(key, x => create()); } public static TValue GetOrStore<TValue>(this Cache cache, string key, Func<string, TValue> create, DateTime absoluteExpiration, TimeSpan slidingExpiration) { var instance = cache.GetOrStoreContainer<TValue>(key, absoluteExpiration, slidingExpiration); if (instance.Value == null) lock (instance) if (instance.Value == null) instance.Value = create(key); return instance.Value; } private static Container<TValue> GetOrStoreContainer<TValue>(this Cache cache, string key, DateTime absoluteExpiration, TimeSpan slidingExpiration) { var instance = cache[key]; if (instance == null) lock (cache) { instance = cache[key]; if (instance == null) { instance = new Container<TValue>(); cache.Add(key, instance, null, absoluteExpiration, slidingExpiration, CacheItemPriority.Default, null); } } return (Container<TValue>)instance; } } 

其他一些答案在这里不涉及以下内容:

  • caching踩踏
  • 双重检查locking

这可能会导致生成器(可能需要很长时间)在不同的线程中运行多次。

这是我的版本, 不应该遭受这个问题:

 // using System.Web.Caching; public static class CacheExtensions { private static object sync = new object(); private static TimeSpan defaultExpire = TimeSpan.FromMinutes(20); public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator) => cache.GetOrStore(key, generator, defaultExpire); public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator, TimeSpan expire) { var result = cache[key]; if (result == null) { lock (sync) { result = cache[key]; if (result == null) { result = generator(); cache.Insert(key, result, null, DateTime.Now.AddMinutes(expire.TotalMinutes), Cache.NoSlidingExpiration); } } } return (T)result; } }