如何在ASP中缓存对象。净MVC吗?

时间:2022-04-28 03:50:13

I'd like to cache objects in ASP.NET MVC. I have a BaseController that I want all Controllers to inherit from. In the BaseController there is a User property that will simply grab the User data from the database so that I can use it within the controller, or pass it to the views.

我想在ASP中缓存对象。净MVC。我有一个BaseController,我希望所有的控制器都继承它。在BaseController中,有一个用户属性可以从数据库中获取用户数据,这样我就可以在控制器中使用它,或者将它传递给视图。

I'd like to cache this information. I'm using this information on every single page so there is no need to go to the database each page request.

我想缓存这些信息。我在每个页面上使用这些信息,所以不需要访问数据库每个页面请求。

I'd like something like:

我想这样:

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

How do I implement simple caching in ASP.NET MVC?

如何在ASP中实现简单的缓存。净MVC吗?

8 个解决方案

#1


66  

You can still use the cache (shared among all responses) and session (unique per user) for storage.

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

I like the following "try get from cache/create and store" pattern (c#-like pseudocode):

我喜欢下面的“尝试从缓存/创建和存储中获取”模式(类似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;
  }
}

you'd use this like so:

你可以这样使用:

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

You can adapt this pattern to the Session, ViewState (ugh) or any other cache mechanism. You can also extend the ControllerContext.HttpContext (which I think is one of the wrappers in System.Web.Extensions), or create a new class to do it with some room for mocking the cache.

您可以将此模式调整为会话、ViewState (ugh)或任何其他缓存机制。您还可以扩展ControllerContext。HttpContext(我认为它是System.Web.Extensions中的一个包装器),或者创建一个新的类来实现它,并为模仿缓存预留一些空间。

#2


58  

I took Will's answer and modified it to make the CacheExtensions class static and to suggest a slight alteration in order to deal with the possibility of Func<T> being null :

我接受了Will的回答,并对其进行了修改,使CacheExtensions类变为静态,并建议进行轻微的修改,以处理Func 为空的可能性:

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;

    }

}

I would also consider taking this a step further to implement a testable Session solution that extends the System.Web.HttpSessionStateBase abstract class.

我还将考虑进一步实现扩展System.Web的可测试会话解决方案。HttpSessionStateBase抽象类。

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

}

#3


5  

If you want it cached for the length of the request, put this in your controller base class:

如果希望缓存请求的长度,请将其放入控制器基类:

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

If you want to cache for longer, use the replace the ControllerContext call with one to Cache[]. If you do choose to use the Cache object to cache longer, you'll need to use a unique cache key as it will be shared across requests/users.

如果您想要缓存更长的时间,请使用一个“ControllerContext调用”来缓存[]。如果您选择使用缓存对象来缓存更长的时间,您将需要使用一个惟一的缓存键,因为它将在请求/用户之间共享。

#4


3  

I like to hide the fact that the data is cached in the repository. You can access the cache through the HttpContext.Current.Cache property and store the User information using "User"+id.ToString() as the key.

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

This means that all access to the User data from the repository will use cached data if available and requires no code changes in the model, controller, or view.

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

I have used this method to correct serious performance problems on a system that was querying the database for each User property and reduced page load times from minutes to single digit seconds.

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

#5


3  

@njappboy: Nice implementation. I would only defer the Generator( ) invocation until the last responsible moment. thus you can cache method invocations too.

@njappboy:很好的实现。我只会将Generator()调用延迟到最后负责的时刻。因此,您也可以缓存方法调用。

/// <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;
}

#6


3  

A couple of the other answers here don't deal with the following:

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

  • cache stampede
  • 缓存踩踏事件
  • double check lock
  • 双重检查锁定

This could lead to the generator (which could take a long time) running more than once in different threads.

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

Here's my version that shouldn't suffer from this problem:

以下是我的版本,不应该受到这个问题的困扰:

// 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;
    }
}

#7


2  

If you don't need specific invalidation features of ASP.NET caching, static fields are pretty good, lightweight and easy to use. However, as soon as you needed the advanced features, you can switch to ASP.NET's Cache object for storage.

如果您不需要ASP的具体失效特征。NET缓存、静态字段都非常好、轻量级和易于使用。但是,只要您需要高级特性,就可以切换到ASP。NET的缓存对象。

The approach I use is to create a property and a private field. If the field is null, the property will fill it and return it. I also provide an InvalidateCache method that manually sets the field to null. The advantage of this approach it that the caching mechanism is encapsulated in the property and you can switch to a different approach if you want.

我使用的方法是创建属性和私有字段。如果字段为空,属性将填充它并返回它。我还提供了一个InvalidateCache方法,该方法手动将字段设置为null。这种方法的优点是缓存机制封装在属性中,如果需要,可以切换到其他方法。

#8


1  

Implementation with a minimal cache locking. The value stored in the cache is wrapped in a container. If the value is not in the cache, then the value container is locked. The cache is locked only during the creation of the container.

具有最小缓存锁定的实现。存储在缓存中的值被包装在一个容器中。如果值不在缓存中,则值容器被锁定。缓存仅在创建容器期间被锁定。

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

#1


66  

You can still use the cache (shared among all responses) and session (unique per user) for storage.

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

I like the following "try get from cache/create and store" pattern (c#-like pseudocode):

我喜欢下面的“尝试从缓存/创建和存储中获取”模式(类似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;
  }
}

you'd use this like so:

你可以这样使用:

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

You can adapt this pattern to the Session, ViewState (ugh) or any other cache mechanism. You can also extend the ControllerContext.HttpContext (which I think is one of the wrappers in System.Web.Extensions), or create a new class to do it with some room for mocking the cache.

您可以将此模式调整为会话、ViewState (ugh)或任何其他缓存机制。您还可以扩展ControllerContext。HttpContext(我认为它是System.Web.Extensions中的一个包装器),或者创建一个新的类来实现它,并为模仿缓存预留一些空间。

#2


58  

I took Will's answer and modified it to make the CacheExtensions class static and to suggest a slight alteration in order to deal with the possibility of Func<T> being null :

我接受了Will的回答,并对其进行了修改,使CacheExtensions类变为静态,并建议进行轻微的修改,以处理Func 为空的可能性:

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;

    }

}

I would also consider taking this a step further to implement a testable Session solution that extends the System.Web.HttpSessionStateBase abstract class.

我还将考虑进一步实现扩展System.Web的可测试会话解决方案。HttpSessionStateBase抽象类。

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

}

#3


5  

If you want it cached for the length of the request, put this in your controller base class:

如果希望缓存请求的长度,请将其放入控制器基类:

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

If you want to cache for longer, use the replace the ControllerContext call with one to Cache[]. If you do choose to use the Cache object to cache longer, you'll need to use a unique cache key as it will be shared across requests/users.

如果您想要缓存更长的时间,请使用一个“ControllerContext调用”来缓存[]。如果您选择使用缓存对象来缓存更长的时间,您将需要使用一个惟一的缓存键,因为它将在请求/用户之间共享。

#4


3  

I like to hide the fact that the data is cached in the repository. You can access the cache through the HttpContext.Current.Cache property and store the User information using "User"+id.ToString() as the key.

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

This means that all access to the User data from the repository will use cached data if available and requires no code changes in the model, controller, or view.

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

I have used this method to correct serious performance problems on a system that was querying the database for each User property and reduced page load times from minutes to single digit seconds.

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

#5


3  

@njappboy: Nice implementation. I would only defer the Generator( ) invocation until the last responsible moment. thus you can cache method invocations too.

@njappboy:很好的实现。我只会将Generator()调用延迟到最后负责的时刻。因此,您也可以缓存方法调用。

/// <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;
}

#6


3  

A couple of the other answers here don't deal with the following:

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

  • cache stampede
  • 缓存踩踏事件
  • double check lock
  • 双重检查锁定

This could lead to the generator (which could take a long time) running more than once in different threads.

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

Here's my version that shouldn't suffer from this problem:

以下是我的版本,不应该受到这个问题的困扰:

// 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;
    }
}

#7


2  

If you don't need specific invalidation features of ASP.NET caching, static fields are pretty good, lightweight and easy to use. However, as soon as you needed the advanced features, you can switch to ASP.NET's Cache object for storage.

如果您不需要ASP的具体失效特征。NET缓存、静态字段都非常好、轻量级和易于使用。但是,只要您需要高级特性,就可以切换到ASP。NET的缓存对象。

The approach I use is to create a property and a private field. If the field is null, the property will fill it and return it. I also provide an InvalidateCache method that manually sets the field to null. The advantage of this approach it that the caching mechanism is encapsulated in the property and you can switch to a different approach if you want.

我使用的方法是创建属性和私有字段。如果字段为空,属性将填充它并返回它。我还提供了一个InvalidateCache方法,该方法手动将字段设置为null。这种方法的优点是缓存机制封装在属性中,如果需要,可以切换到其他方法。

#8


1  

Implementation with a minimal cache locking. The value stored in the cache is wrapped in a container. If the value is not in the cache, then the value container is locked. The cache is locked only during the creation of the container.

具有最小缓存锁定的实现。存储在缓存中的值被包装在一个容器中。如果值不在缓存中,则值容器被锁定。缓存仅在创建容器期间被锁定。

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