我有一个简单的ASP.NET MVC
控制器。在一些操作方法中,我访问了一个资源,我说是昂贵的。
所以我想,为什么不让它静止。所以我没有做double checked locking,而是认为我可以在.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);
}
}
答案 0 :(得分:5)
在您的示例中,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);
}
}
答案 1 :(得分:4)
我同意Greg的说法Lazy&lt;&gt;这里不合适。
您可以尝试使用asp.net caching来缓存文件夹的内容,使用_foo.PathToFiles作为密钥。这具有优于Lazy&lt;&gt;的优点。您可以控制缓存的生命周期,以便它每天或每周重新获取内容,而无需重新启动应用程序。
此外,缓存对您的服务器也很友好,因为如果没有足够的内存来支持它,它会正常降级。
答案 2 :(得分:2)
Lazy<T>
在您不确定是否需要资源时效果最佳,因此只有在实际需要时才会及时加载。{p> 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);
}
效果最佳。
该操作总是会加载资源,但是因为它很昂贵,你可能想把它缓存到某个地方?你可以尝试这样的事情:
{{1}}
答案 3 :(得分:1)
我刚刚遇到了您描述的相同问题,因此我创建了一个班级CachedLazy<T>
- &gt;允许在控制器实例之间共享值,但具有可选的定时到期和一次性创建,与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;
}
}