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

Kur*_*ula 9 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;
}
Run Code Online (Sandbox Code Playgroud)

是否还有其他最佳实践来加载所有页面的所有默认值并缓存它们?

Nig*_*888 5

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

另外,我建议不要使用基本控制器类。这肯定意味着您将在同一个基本控制器中混合功能 a功能 b以及功能 c的逻辑 - 您正在创建一个上帝对象。MVC有更好的方法——过滤器可以注册为所有操作运行,并且如果您将它们与过滤器结合使用,则可以在特定操作上使用它们。

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

我的缓存过滤器

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

MyCacheFilter做了三件事:

  1. 从缓存中读取数据
  2. 检查数据是否存在,如果不存在则重新加载缓存
  3. 将对数据对象的引用添加到请求缓存,可以从应用程序中的任何其他位置(包括视图)访问它。

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

HttpContextBase扩展

为了使缓存的数据对于应用程序的其余部分来说是类型安全的,这里有一些方便的扩展方法。

/// <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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

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

<p>
    value1: @this.Context.GetMyDefault("value1")<br />
    value2: @this.Context.GetMyDefault("value2")<br />
    value3: @this.Context.GetMyDefault("value3")<br />
</p>
Run Code Online (Sandbox Code Playgroud)

我已经在此 GitHub 存储库中创建了该解决方案的工作演示。

再说一次,这不是唯一的方法。您可能希望针对您的应用程序对此进行一些更改。例如,您可以使用ViewData将数据返回到视图而不是HttpContextBase.Items. 或者您可能希望摆脱全局过滤器并将缓存模式移至从缓存加载/返回数据的单个扩展方法中。确切的解决方案取决于您在问题中未提供的内容 - 要求。