如何在 ASP.NET MVC 控制器中使用 Lazy

本文关键字:Lazy 控制器 ASP NET MVC | 更新日期: 2023-09-27 18:09:05

我有一个简单的ASP.NET MVC控制器。在几个操作方法中,我访问了一个资源,我会说它很昂贵

所以我想,为什么不让它成为静态的。因此,我认为我可以在 .NET 4.0 中利用Lazy<T>的使用,而不是进行双重检查锁定。调用昂贵的服务一次而不是多次。

所以,如果这是我的 pseduo 代码,我该如何更改它,请使用 Lazy<T> .对于这个悔恨的例子,我将使用 File System 作为昂贵的资源因此,在这个示例中,每次请求调用该 ActionMethod 时,我都希望使用 Lazy 来保存该文件列表,而不是从目标路径获取所有文件。当然,这只是第一次打电话。

下一个假设:如果内容被更改,请不要担心。这超出了范围,在这里。

public class FooController : Controller
{
    private readonly IFoo _foo;
    public FooController(IFoo foo)
    {
        _foo = foo;
    }
    public ActionResult PewPew()
    {
        // Grab all the files in a folder.
        // nb. _foo.PathToFiles = "/Content/Images/Harro"
        var files = Directory.GetFiles(Server.MapPath(_foo.PathToFiles));
        // Note: No, I wouldn't return all the files but a concerete view model
        //       with only the data from a File object, I require.
        return View(files);
    }
}

如何在 ASP.NET MVC 控制器中使用 Lazy<T>

在您的示例中,Directory.GetFiles 的结果取决于 _foo 的值,该值不是静态的。因此,不能将 Lazy<string[]> 的静态实例用作控制器所有实例之间的共享缓存。

ConcurrentDictionary<TKey, TValue>听起来更接近您想要的东西。

// Code not tested, blah blah blah...
public class FooController : Controller
{
    private static readonly ConcurrentDictionary<string, string[]> _cache
        = new ConcurrentDictionary<string, string[]>();
    private readonly IFoo _foo;
    public FooController(IFoo foo)
    {
        _foo = foo;
    }
    public ActionResult PewPew()
    {
        var files = _cache.GetOrAdd(Server.MapPath(_foo.PathToFiles), path => {
            return Directory.GetFiles(path);
        });
        return View(files);
    }
}

我同意格雷格的观点,懒惰<>在这里是不合适的。

您可以尝试使用 asp.net 缓存来缓存文件夹的内容,使用 _foo。路径到文件作为您的密钥。与 Lazy 相比<>这有一个优势,您可以控制缓存的生命周期,因此它将每天或每周重新获取内容,而无需重新启动应用程序。

此外,缓存对您的服务器很友好,因为如果没有足够的内存来支持它,它将优雅地降级。

当您

不确定是否需要资源时,Lazy<T>效果最好,因此仅在实际需要时才及时加载它。无论如何,该操作始终会加载资源,但是因为它很昂贵,您可能希望将其缓存在某个地方? 你可以尝试这样的事情:

public ActionResult PewPew()
{
    MyModel model;
    const string cacheKey = "resource";
    lock (controllerLock)
    {
        if (HttpRuntime.Cache[cacheKey] == null)
        {
            HttpRuntime.Cache.Insert(cacheKey, LoadExpensiveResource());
        }
        model = (MyModel) HttpRuntime.Cache[cacheKey];
    }
    return View(model);
}

我刚刚遇到了您描述的相同问题,所以我创建了一个类CachedLazy<T> -> 允许在控制器实例之间共享值,但与ConcurrentDictionary不同,具有可选的定时到期和一次性创建。

/// <summary>
/// Provides a lazily initialised and HttpRuntime.Cache cached value.
/// </summary>
public class CachedLazy<T>
{
    private readonly Func<T> creator;
    /// <summary>
    /// Key value used to store the created value in HttpRuntime.Cache
    /// </summary>
    public string Key { get; private set; }
    /// <summary>
    /// Optional time span for expiration of the created value in HttpRuntime.Cache
    /// </summary>
    public TimeSpan? Expiry { get; private set; }
    /// <summary>
    /// Gets the lazily initialized or cached value of the current Cached instance.
    /// </summary>
    public T Value
    {
        get
        {
            var cache = HttpRuntime.Cache;
            var value = cache[Key];
            if (value == null)
            {
                lock (cache)
                {
                    // After acquiring lock, re-check that the value hasn't been created by another thread
                    value = cache[Key];
                    if (value == null)
                    {
                        value = creator();
                        if (Expiry.HasValue)
                            cache.Insert(Key, value, null, Cache.NoAbsoluteExpiration, Expiry.Value);
                        else
                            cache.Insert(Key, value);
                    }
                }
            }
            return (T)value;
        }
    }
    /// <summary>
    /// Initializes a new instance of the CachedLazy class. If lazy initialization occurs, the given
    /// function is used to get the value, which is then cached in the HttpRuntime.Cache for the 
    /// given time span.
    /// </summary>
    public CachedLazy(string key, Func<T> creator, TimeSpan? expiry = null)
    {
        this.Key = key;
        this.creator = creator;
        this.Expiry = expiry;
    }
}