2011-09-21 8 views
5

私はシンプルなASP.NET MVCコントローラを持っています。いくつかのアクションメソッドの中で、私はが高価であると言うリソースにアクセスします。ASP.NET MVCコントローラでLazy <T>を使用するにはどうすればよいですか?

だから私はそれを静的にしてみてはいかがでしょうか。したがって、double checked lockingを実行する代わりに、私はLazy<T>の使用を.NET 4.0で活用できると考えました。 高価なサービスを複数回ではなく1回呼び出します。

これが私の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); 
    } 
} 
+1

ASP.NETキャッシュを使用すると何が問題になりますか? – tvanfosson

+1

オブジェクトの怠惰なインスタンス化ではなく、シングルトンを探しているようです。もちろん、*レイジーを使ってシングルトンを作ることができます。 –

答えて

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); 
    } 
} 
+0

これは素晴らしい考えです!キャッシュオプション(MartinまたはChris)によって、複数のリクエストがキャッシュに挿入しようとする可能性があります。リクエストが同時に発生していても、それは正しいでしょうか?(競合状態のもの)、並行ディクショナリは第2、第3の要求(同時に)が高価なリソースを実行するのを防ぐのですか? –

+0

@Pure - キャッシュに追加される前に複数のスレッドが値を取得しようとすると、ConcurrentDictionaryは「valueFactory」関数を複数回呼び出すことがあります。 ConcurrentDictionaryはスレッドセーフです(キーごとに1つの値しか追加されません)が、valueFactoryの呼び出しはロック内では発生しません。 – Greg

+0

ConcurrentDictionaryはスレッドセーフですが、GetOrAddおよびAddOrUpdateに渡されたデリゲートはディクショナリの内部ロックの外で呼び出されます。 More here:http://msdn.microsoft.com/en-us/library/dd997369.aspx – Paul

4

ここでは、Lazy <>が不適切であることに同意します。

asp.net cachingを使用して、_foo.PathToFilesをキーとしてフォルダの内容をキャッシュすることができます。これは、Lazy <よりも利点があります。キャッシングの有効期間を制御することで、アプリケーションを再起動することなく毎日または毎週の内容を再フェッチします。

キャッシュは、サーバーをサポートするのに十分なメモリがない場合は正常に機能しなくなります。

+0

+1組み込みのASP.NETキャッシュを推奨します。そうする強固な理由があるまで、車輪を再構築しないでください。 – Greg

+0

ありがとうございます。私はあなたのインラインキャッシュも好きです - このシナリオではないかもしれませんが、Windowsフォームアプリケーションには便利かもしれません。 –

2

Lazy<T>は、リソースが必要かどうかわからない場合に最適です。実際に必要なときにジャストインタイムで読み込まれます。 アクションは常にリソースをロードするつもりですが、高価なのでどこかにキャッシュしたいと思っていますか? >は、値はコントローラのインスタンス間で共有することができますが、オプションの時限満了とワンタイムの作成とは異なり - 私はちょうどあなたがそう、私はクラスCachedLazy<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); 
} 
1

:あなたはこのような何かを試みることができます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; 
    } 
} 
関連する問題