2011-09-21 18 views
5

मुझे एक साधारण ASP.NET MVC नियंत्रक मिला है। कुछ क्रिया विधियों के अंदर, मैं एक संसाधन तक पहुंचता हूं जो मैं कहूंगा महंगा है।मैं एएसपी.नेट एमवीसी नियंत्रक में आलसी <T> का उपयोग कैसे कर सकता हूं?

तो मैंने सोचा, क्यों इसे स्थिर नहीं बनाते। तो double checked locking करने के बजाय मैंने सोचा कि मैं .NET 4.0 में Lazy<T> के उपयोग का लाभ उठा सकता हूं। कई बार के बजाय महंगा सेवा पर कॉल करें।

तो, यदि यह मेरा pseduo कोड है, तो मैं इसे कैसे बदल सकता हूं Lazy<T> का उपयोग करें। इस contrite उदाहरण के लिए, मैं महंगा संसाधन के रूप में उपयोग करूंगा तो इस उदाहरण के साथ, गंतव्य पथ से सभी फ़ाइलों को प्राप्त करने के बजाय, जब भी एक अनुरोध उस क्रिया विधि को कॉल करता है, तो मैं आलसी का उपयोग करने की उम्मीद कर रहा था फाइलों की सूची पकड़ो .. निश्चित रूप से, कॉल केवल पहली बार कॉल करता है।

अगला धारणा: सामग्री बदल दी गई है तो चिंता न करें। यह दायरे से बाहर है, यहाँ।

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

एएसपी.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

यह एक अच्छा विचार है! कैश विकल्प (मार्टिन या क्रिस द्वारा दिया गया) के साथ, यह संभव है कि एक से अधिक अनुरोध कैश में डालने का प्रयास करेंगे- अगर- एक ही समय में अनुरोध हो रहे हैं (अधिक या कम) .. सही?(रेस कंडीशन सॉर्टा चीज) जबकि समवर्ती शब्दकोश महंगे संसाधन को निष्पादित करने से दूसरे, तीसरे अनुरोध (एक ही समय में) को रोकता है, है ना? –

+0

@ शुद्ध - असल में नहीं, ConcurrentDictionary कैश में जोड़े जाने से पहले मान प्राप्त करने का प्रयास करते समय एक से अधिक बार "वैल्यूफैक्टरी" फ़ंक्शन का आह्वान कर सकता है। ConcurrentDictionary थ्रेड सुरक्षित है (प्रत्येक कुंजी के लिए केवल एक मान जोड़ा जाएगा), लेकिन वैलफैक्टरी का आविष्कार लॉक के अंदर नहीं होता है। – Greg

+0

ConcurrentDictionary थ्रेड सुरक्षित है हालांकि प्रतिनिधि को GetOrAdd पास किया गया है और AddOrUpdate को शब्दकोश के आंतरिक लॉक के बाहर बुलाया जाता है। यहां और अधिक: http://msdn.microsoft.com/en-us/library/dd997369.aspx – Paul

4

मैं ग्रेग से सहमत हूं कि Lazy <> यहां अनुचित है।

आप _foo.PathToFiles को अपनी कुंजी के रूप में उपयोग करके, फ़ोल्डर की सामग्री को कैश करने के लिए asp.net caching का उपयोग करने का प्रयास कर सकते हैं। आलसी < पर इसका लाभ है> कि आप कैशिंग के जीवनकाल को नियंत्रित कर सकते हैं ताकि यह सामग्री को पुन: प्रारंभ करने के लिए हर दिन या हर हफ्ते कहें।

इसके अलावा कैशिंग आपके सर्वर के अनुकूल है, अगर इसका समर्थन करने के लिए पर्याप्त स्मृति नहीं है तो यह शानदार रूप से अव्यवस्थित हो जाएगा।

+0

+1। जब तक आपके पास ऐसा करने का ठोस कारण न हो तब तक पहिया को पुन: पेश न करें। – Greg

+0

हाँ धन्यवाद। मुझे आपकी इनलाइन कैश भी पसंद है - शायद इस परिदृश्य के लिए नहीं, लेकिन विंडोज़ फॉर्म ऐप्स के लिए उपयोगी हो सकता है। –

2

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); 
} 
1

मैं सिर्फ एक ही समस्या आप ऐसा वर्णित मैं एक वर्ग 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; 
    } 
}