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

C# 如何使用Lazy<;T>;在ASP.NET MVC控制器中?,c#,.net,asp.net,asp.net-mvc-3,lazy-loading,C#,.net,Asp.net,Asp.net Mvc 3,Lazy Loading,我有一个简单的ASP.netmvc控制器。在一些操作方法中,我访问了一个资源,我会说它很昂贵 所以我想,为什么不让它静止。因此,我认为我可以利用.NET4.0中的Lazy,而不是这样做。呼叫昂贵的服务一次,而不是多次 因此,如果这是我的psedoo代码,我如何使用Lazy更改它。 对于这个懊悔的例子,我将使用文件系统作为昂贵的资源 因此,在这个示例中,每次请求调用ActionMethod时,我都希望使用Lazy保存文件列表,而不是从目标路径获取所有文件。。当然,这只是第一次打电话 下一个假设:不

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

所以我想,为什么不让它静止。因此,我认为我可以利用.NET4.0中的
Lazy
,而不是这样做。呼叫昂贵的服务一次,而不是多次

因此,如果这是我的psedoo代码,我如何使用
Lazy
更改它。 对于这个懊悔的例子,我将使用
文件系统作为昂贵的资源
因此,在这个示例中,每次请求调用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);
    }
}

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

这听起来更接近你想要的东西

// 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);
    }
}
//代码未测试,诸如此类。。。
公共类控制器:控制器
{
专用静态只读ConcurrentDictionary\u缓存
=新的ConcurrentDictionary();
私有只读iFooFoo;
公共食品控制员(IFoo-foo)
{
_foo=foo;
}
公共行动结果PewPew()
{
var files=\u cache.GetOrAdd(Server.MapPath(\u foo.PathToFiles),path=>{
返回目录.GetFiles(路径);
});
返回视图(文件);
}
}

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

您可以尝试使用来缓存文件夹的内容,使用_foo.PathToFiles作为密钥。与Lazy相比,它有一个优势,即您可以控制缓存的生存期,这样它就可以每天或每周重新读取内容,而无需重新启动应用程序

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

Lazy
在您不确定是否需要资源时效果最好,因此只有在实际需要时才及时加载。 不管怎么说,操作总是要加载资源,但是因为它很昂贵,您可能想将它缓存到某个地方?您可以尝试以下方法:

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
->允许在控制器实例之间共享值,但与
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;
    }
}
//
///提供延迟初始化的HttpRuntime.Cache缓存值。
/// 
公共类CachedLazy
{
私有只读函数创建者;
/// 
///用于将创建的值存储在HttpRuntime.Cache中的键值
/// 
公共字符串密钥{get;private set;}
/// 
///HttpRuntime.Cache中创建的值过期的可选时间范围
/// 
公共时间跨度?到期{get;private set;}
/// 
///获取当前缓存实例的延迟初始化或缓存值。
/// 
公共价值
{
得到
{
var cache=HttpRuntime.cache;
var值=缓存[键];
如果(值==null)
{
锁(缓存)
{
//获取锁后,重新检查该值是否不是由另一个线程创建的
值=缓存[键];
如果(值==null)
{
value=creator();
if(Expiry.HasValue)
Insert(Key,value,null,cache.noabsoluteexption,expiration.value);
其他的
cache.Insert(键、值);
}
}
}
返回(T)值;
}
}
/// 
///初始化CachedLazy类的新实例
///函数用于获取值,然后将该值缓存在
///给定的时间跨度。
/// 
public CachedLazy(字符串键,Func创建者,TimeSpan?expiry=null)
{
这个。键=键;
this.creator=创建者;
这个。到期=到期;
}
}

使用ASP.NET缓存有什么问题吗?听起来你在寻找一个单例,而不是对象的惰性实例化。当然,您可以使用
Lazy
创建一个singleton…+1来推荐内置的ASP.NET缓存。除非你有充分的理由这样做,否则不要重新发明轮子。是的,谢谢。我也喜欢你的内联缓存-可能不适用于这种情况,但可能对windows窗体应用程序有用。这是一个好主意!使用缓存选项(由Martin或Chris提供),可能会有多个请求尝试插入到缓存中—如果—请求同时发生(或多或少)。。正确的?(竞争条件排序)当并发字典阻止第二、第三个请求(同时)执行昂贵的资源时,对吗?@Pure-实际上不是,如果多个线程在将值添加到缓存之前尝试获取该值,那么ConcurrentDictionary可能会多次调用“valueFactory”函数。ConcurrentDictionary是线程安全的(每个键只会添加一个值),但valueFactory的调用不会发生在锁内。ConcurrentDictionary是线程安全的,但是传递给GetOrAdd和AddOrUpdate的委托在字典的内部锁外被调用。更多信息:因此,如果您只是在工厂方法中创建了一个
Lazy
,就可以避免多次执行昂贵的操作。