2011-09-21 29 views
5

Tôi có bộ điều khiển đơn giản ASP.NET MVC. Bên trong một vài phương pháp hành động, tôi truy cập vào một tài nguyên mà tôi sẽ nói là đắt tiền.Làm thế nào tôi có thể sử dụng Lazy <T> trong bộ điều khiển ASP.NET MVC?

Vì vậy, tôi nghĩ, tại sao không làm cho nó tĩnh. Vì vậy, thay vì làm double checked locking Tôi nghĩ rằng tôi có thể tận dụng việc sử dụng Lazy<T> trong .NET 4.0. Gọi số dịch vụ đắt tiền một lần thay vì nhiều lần.

Vì vậy, nếu đây là mã pseduo của tôi, làm thế nào tôi có thể thay đổi nó sử dụng Lazy<T>. Ví dụ này, tôi sẽ sử dụng File Systemlà tài nguyên đắt tiền Vì vậy, với ví dụ này, thay vì nhận tất cả các tệp từ đường dẫn đích, mỗi lần yêu cầu gọi ActionMethod, tôi hy vọng sẽ sử dụng giữ danh sách tệp đó .. tất nhiên, chỉ thực hiện cuộc gọi lần đầu tiên.

Giả định tiếp theo: đừng lo lắng nếu nội dung bị thay đổi. Đó là phạm vi, ở đây.

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

Có gì sai khi sử dụng bộ nhớ cache ASP.NET? – tvanfosson

+1

Nghe có vẻ như bạn đang tìm kiếm một singleton, chứ không phải là lười biếng instantiation của một đối tượng. Tất nhiên, bạn có thể * sử dụng * 'Lazy' để tạo ra một singleton ... –

Trả lời

5

Trong ví dụ của bạn, kết quả của Directory.GetFiles phụ thuộc vào giá trị của _foo, mà không phải là tĩnh. Vì vậy, bạn không thể sử dụng một thể hiện tĩnh của Lazy<string[]> làm bộ nhớ cache dùng chung giữa tất cả các phiên bản của trình điều khiển của bạn.

Âm thanh ConcurrentDictionary<TKey, TValue> giống như nội dung nào đó gần với nội dung bạn muốn.

// 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

Đây là một ý tưởng tuyệt vời! Với tùy chọn bộ nhớ cache (được đưa ra bởi Martin hoặc Chris), có thể nhiều hơn một yêu cầu sẽ cố gắng chèn vào bộ nhớ cache -if- các yêu cầu đang xảy ra (nhiều hơn hoặc ít hơn) cùng một lúc .. phải không?(điều kiện phân loại điều kiện chủng tộc) trong khi từ điển đồng thời ngăn yêu cầu thứ 2, thứ 3 (cùng một lúc) thực hiện tài nguyên đắt tiền, phải không? –

+0

@Pure - Trên thực tế không, ConcurrentDictionary có thể gọi hàm "valueFactory" nhiều lần nếu nhiều chủ đề cố gắng lấy giá trị trước khi nó được thêm vào bộ nhớ cache. ConcurrentDictionary là chủ đề an toàn (chỉ một giá trị sẽ được thêm cho mỗi khóa), nhưng việc gọi valueFactory không xảy ra bên trong một khóa. – Greg

+0

ConcurrentDictionary là luồng an toàn, tuy nhiên ủy nhiệm được chuyển đến GetOrAdd và AddOrUpdate được gọi bên ngoài khóa nội bộ của từ điển. Thêm vào đây: http://msdn.microsoft.com/en-us/library/dd997369.aspx – Paul

4

Tôi đồng ý với Greg rằng Lazy <> không phù hợp ở đây.

Bạn có thể thử sử dụng asp.net caching để lưu vào bộ nhớ cache nội dung của một thư mục bằng cách sử dụng _foo.PathToFiles làm khóa của bạn. Điều này có một lợi thế hơn Lazy <> mà bạn có thể kiểm soát tuổi thọ của bộ nhớ đệm để nó sẽ nạp lại nội dung nói mỗi ngày hoặc mỗi tuần mà không yêu cầu khởi động lại ứng dụng.

Cũng lưu vào bộ nhớ cache thân thiện với máy chủ của bạn ở chỗ nó sẽ giảm xuống một cách duyên dáng nếu không có đủ bộ nhớ để hỗ trợ nó.

+0

+1 để đề xuất bộ nhớ đệm ASP.NET tích hợp sẵn. Đừng sáng tạo lại bánh xe cho đến khi bạn có lý do vững chắc để làm như vậy. – Greg

+0

Có, cảm ơn. Tôi thích bộ nhớ cache nội tuyến của bạn quá - có thể không phải cho kịch bản này nhưng có thể hữu ích cho các cửa sổ hình thức ứng dụng. –

2

Lazy<T> hoạt động tốt nhất khi bạn không chắc chắn liệu mình có cần tài nguyên hay không, vì vậy nó chỉ được tải đúng lúc khi thực sự cần thiết. Hành động sẽ luôn tải tài nguyên bất kể, nhưng vì nó đắt tiền, bạn có thể muốn lưu nó ở đâu đó? Bạn có thể thử một cái gì đó như thế này:

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

Tôi chỉ có cùng một vấn đề bạn mô tả vì vậy tôi tạo ra một lớp CachedLazy<T> -> cho phép các giá trị được chia sẻ giữa các trường hợp điều khiển, nhưng với hạn sử dụng theo thời gian tùy chọn và tạo một lần không giống như 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; 
    } 
} 
Các vấn đề liên quan