基本控制器中的Asp.Net MVC Outputcache无法正常工作

时间:2017-01-29 06:29:58

标签: c# asp.net asp.net-mvc asp.net-mvc-4

我在asp.net mvc基本控制器中使用输出缓存属性,但每次都在OnActionExecuting中调用它。是否有任何选项只能一次调用该方法来加载所有默认值?

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    GetDefaults();
    base.OnActionExecuting(filterContext);
}

[OutputCache(Duration = 60000)]
private ActionResult GetDefaults()
{
    //code to load all my default values
    // Viewdata values used in all pages in my aplication
    // Viewdata values used in all pages in my aplication
    // Viewdata values used in all pages in my aplication
    return null;
}

为所有页面加载所有默认值并缓存它们的其他最佳做法是什么?

3 个答案:

答案 0 :(得分:4)

您可以使用Global.asax.cs中的Application_Start()或Session_Start()仅存储一次数据,具体取决于您是希望在应用程序启动时还是在每个会话启动时刷新数据。这取决于您的应用程序需要做什么。

如果您需要为每个操作执行某些操作,那么拥有像您一样的基本控制器是很好的。最常见的是您需要为每个操作执行的[授权]过滤器,以查看用户是否出于安全原因而获得授权。 另一种方法是编写自己的ActionFilterAttribute并执行您需要执行的缓存部分。然后,您需要做的就是将此新的Action过滤器添加到执行此缓存所需的任何操作。请在此处查看如何执行此操作:https://msdn.microsoft.com/en-us/library/dd410056(v=vs.98).aspx但是,因为您只想加载一次数据,我认为操作过滤器可能不适合进行缓存。

答案 1 :(得分:3)

如果你想要的只是缓存数据,那么OutputCache就不是正确的机制。 OutputCache可用于缓存由操作方法生成的HTML,因此不必重新生成。如果要缓存数据,可以使用HttpContextBase.Cache轻松完成。

另外,我recommend against using a base controller class。这肯定意味着您将在同一个基本控制器中混合功能功能b 功能c 的逻辑 - 您正在创建一个god object。 MVC有一个更好的方法 - filters可以注册为所有操作运行,如果你将它们与过滤器结合使用,可以用于特定的操作。

虽然,对缓存使用全局过滤器可能没有任何意义,因为通常数据在请求时缓存 ,我已经创建了一个如何进行演示的演示完成。请注意,缓存是一个非常广泛的主题,可以通过多种方式完成,但由于您提供了无信息有关正在缓存的内容,数据来源或使用方式,我只是将其作为一种可能的方式展示出来。

MyCacheFilter

这里我们有一个执行缓存的动作过滤器。由于动作过滤器保证在视图之前运行,因此这是一种可行的方式。

MyCacheFilter做了三件事:

  1. 从缓存中读取数据
  2. 检查数据是否存在,如果不存在,则重新加载缓存
  3. 将对数据对象的引用添加到请求缓存,可以从应用程序的任何其他位置(包括视图)访问它。
  4. public class MyCacheFilter : IActionFilter
    {
        /// <summary>
        /// The cache key that is used to store/retrieve your default values.
        /// </summary>
        private static string MY_DEFAULTS_CACHE_KEY = "MY_DEFAULTS";
    
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            // Do nothing
        }
    
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var cache = filterContext.HttpContext.Cache;
    
            // This method is called for each request. We check to ensure the cache
            // is initialized, and if not, load the values into it.
            IDictionary<string, string> defaults = 
                cache[MY_DEFAULTS_CACHE_KEY] as IDictionary<string, string>;
            if (defaults == null)
            {
                // The value doesn't exist in the cache, load it
                defaults = GetDefaults();
    
                // Store the defaults in the cache
                cache.Insert(
                    MY_DEFAULTS_CACHE_KEY,
                    defaults,
                    null,
                    DateTime.Now.AddHours(1), // Cache for exactly 1 hour from now
                    System.Web.Caching.Cache.NoSlidingExpiration);
            }
    
            // Caching work is done, now return the result to the view. We can
            // do that by storing it in the request cache.
            filterContext.HttpContext.SetMyDefaults(defaults);
        }
    
        private IDictionary<string, string> GetDefaults()
        {
            // You weren't specific about where your defaults data is coming from
            // or even what data type it is, but you can load it from anywhere in this method
            // and return any data type. The type returned should either by readonly or thread safe.
            var defaults = new Dictionary<string, string>
            {
                { "value1", "testing" },
                { "value2", "hello world" },
                { "value3", "this works" }
            };
    
    
            // IMPORTANT: Cached data is shared throughout the application. You should make
            // sure the data structure that holds is readonly so it cannot be updated.
            // Alternatively, you could make it a thread-safe dictionary (such as ConcurrentDictionary),
            // so it can be updated and the updates will be shared between all users.
            // I am showing a readonly dictionary because it is the safest and simplest way.
            return new System.Collections.ObjectModel.ReadOnlyDictionary<string, string>(defaults);
        }
    }
    

    MyCacheFilter用法

    要使用我们的缓存过滤器并确保在显示任何视图之前填充缓存,我们会将其注册为全局过滤器。全局过滤器非常适合在不同的类中保留单独的功能,可以轻松维护它们(与基本控制器不同)。

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            // Add our MyCacheFilter globally so it runs before every request
            filters.Add(new MyCacheFilter());
            filters.Add(new HandleErrorAttribute());
        }
    }
    

    HttpContextBaseExtensions

    为了使缓存数据对于应用程序的其余部分是安全的,这里有几个方便的扩展方法。

    /// <summary>
    /// Extensions for convenience of using the request cache in views and filters.
    /// Note this is placed in the global namespace so you don't have to import it in your views.
    /// </summary>
    public static class HttpContextBaseExtensions
    {
        /// <summary>
        /// The key that is used to store your context values in the current request cache.
        /// The request cache is simply used here to transfer the cached data to the view.
        /// The difference between the request cache (HttpContext.Items) and HttpContext.Cache is that HttpContext.Items
        /// is immediately released at the end of the request. HttpContext.Cache is stored (in RAM) for the length of
        /// the timeout (or alternatively, using a sliding expiration that keeps it alive for X time after 
        /// the most recent request for it).
        /// 
        /// Note that by using a reference type
        /// this is very efficient. We aren't storing a copy of the data in the request cache, we
        /// are simply storing a pointer to the same object that exists in the cache.
        /// </summary>
        internal static string MY_DEFAULTS_KEY = "MY_DEFAULTS";
    
    
        /// <summary>
        /// This is a convenience method so we don't need to scatter the reference to the request cache key
        /// all over the application. It also makes our cache type safe.
        /// </summary>
        public static string GetMyDefault(this HttpContextBase context, string defaultKey)
        {
            // Get the defaults from the request cache.
            IDictionary<string, string> defaults = context.Items[MY_DEFAULTS_KEY] as IDictionary<string, string>;
    
            // Get the specific value out of the cache that was requested.
            // TryGetValue() is used to prevent an exception from being thrown if the key doesn't
            // exist. In that case, the result will be null
            string result = null;
            defaults.TryGetValue(defaultKey, out result);
    
            return result ?? String.Empty;
        }
    
        /// <summary>
        /// This is a convenience method so we don't need to scatter the reference to the request cache key
        /// all over the application. It also makes our cache type safe.
        /// </summary>
        internal static void SetMyDefaults(this HttpContextBase context, IDictionary<string, string> defaults)
        {
            context.Items[MY_DEFAULTS_KEY] = defaults;
        }
    }
    

    用法

    最后,我们开始使用视图中的数据。由于我们在HttpContextBase对象上有扩展方法,我们需要做的就是通过视图访问它并调用我们的扩展方法。

    <p>
        value1: @this.Context.GetMyDefault("value1")<br />
        value2: @this.Context.GetMyDefault("value2")<br />
        value3: @this.Context.GetMyDefault("value3")<br />
    </p>
    

    我已经创建了此解决方案的工作演示in this GitHub repository

    同样,这不是唯一的方法。您可能希望为您的应用程序稍微改变一下。例如,您可以使用ViewData将数据返回到视图而不是HttpContextBase.Items。或者您可能希望摆脱全局过滤器并将缓存模式移动到单个扩展方法中,该方法从缓存加载/返回数据。确切的解决方案取决于您在问题中未提供的内容 - 要求。

答案 2 :(得分:2)

此处无法利用缓存引擎。原因很明显,如果你考虑为什么它(缓存)实际工作,当一个动作被调用时,它永远不是用户代码调用它,它始终是mvc框架。这样它就有机会应用缓存。在您的示例中,用户代码直接调用方法,此处没有间接,只是一个普通的旧调用 - 不涉及缓存。

相关问题