2011-02-06 7 views
29

मेरे पास एक विशेष पृष्ठ के साथ एक एएसपी.नेट एमवीसी 3 (रेजर) वेब अनुप्रयोग है, जो अत्यधिक डेटाबेस गहन है, और उपयोगकर्ता अनुभव सर्वोच्च प्राथमिकता है।एमवीसी पैटर्न का उल्लंघन किए बिना कैशिंग मॉडल को कैसे कार्यान्वित करें?

इस प्रकार, मैं इस विशेष पृष्ठ पर कैशिंग शुरू कर रहा हूं।

है जैसे कि यह वर्तमान में कैशिंग के बिना है मैं अपने नियंत्रक पतली रखने, जबकि इस कैशिंग पैटर्न लागू करने के लिए एक तरह से यह पता लगाने की कोशिश कर रहा हूँ:

public PartialViewResult GetLocationStuff(SearchPreferences searchPreferences) 
{ 
    var results = _locationService.FindStuffByCriteria(searchPreferences); 
    return PartialView("SearchResults", results); 
} 

आप देख सकते हैं, नियंत्रक बहुत पतली है , जैसा कि इसे होना चाहिए। यह इस बात की परवाह नहीं करता कि यह कैसे/कहां से इसकी जानकारी प्राप्त कर रहा है - यह सेवा का काम है।

नियंत्रण के प्रवाह पर कुछ नोट:

  1. नियंत्रकों एक विशेष सेवा, यह क्षेत्र पर निर्भर DI'ed मिलता है। इस उदाहरण में, इस नियंत्रक प्राप्त एक LocationService
  2. सेवाएं एक IQueryable<T>भंडार के माध्यम से कॉल है और T या ICollection<T> में परिणाम अमल में लाना।

कैसे मैं कैशिंग लागू करना चाहते हैं:

  • मैं आउटपुट कैशिंग का उपयोग नहीं कर सकते हैं - कुछ कारणों के लिए। सबसे पहले, यह क्रिया विधि क्लाइंट-साइड (jQuery/AJAX) से [HttpPost] के माध्यम से लागू की जाती है, जो HTTP मानकों के अनुसार अनुरोध के रूप में कैश नहीं किया जाना चाहिए। दूसरा, मैं HTTP अनुरोध तर्कों के आधार पर पूरी तरह से कैश नहीं करना चाहता - कैश तर्क उस से बहुत जटिल है - वास्तव में दो-स्तर कैशिंग चल रहा है।
  • जैसा कि मैंने उपरोक्त संकेत दिया है, मुझे नियमित डेटा-कैशिंग का उपयोग करने की आवश्यकता है, उदाहरण के लिए Cache["somekey"] = someObj;
  • मैं एक सामान्य कैशिंग तंत्र जहां सेवा के माध्यम से सभी कॉल कैश के माध्यम से पहली जाना लागू करने के लिए नहीं करना चाहते हैं - मैं केवल इस विशेष कार्रवाई विधि पर कैशिंग चाहते हैं।

पहले सोचा है (, पहले कैश चेक, अगर वहाँ db फोन नहीं कैश, वापसी परिणाम में जोड़ने के लिए) मुझे बताओ होगा एक और सेवा बनाने के लिए (जो LocationService विरासत में), और वहाँ कैशिंग कार्यप्रवाह प्रदान करते हैं।

दो समस्याएं हैं यही कारण है कि: - इसके अलावा कुछ भी करने के लिए कोई संदर्भ

  1. सेवाओं बुनियादी कक्षा पुस्तकालय हैं। मुझे यहां System.Web का संदर्भ जोड़ने की आवश्यकता होगी।
  2. मुझे वेब एप्लिकेशन के बाहर HTTP संदर्भ का उपयोग करना होगा, जिसे केवल खराबता माना जाता है, न केवल टेस्टेबिलिटी के लिए, बल्कि सामान्य रूप से - सही?

मैं भी एक मॉडल फ़ोल्डर में एक कैश सेवा वेब अनुप्रयोग (जो मैं वर्तमान में ViewModels के लिए ही इस्तेमाल करते हैं) में Models फ़ोल्डर का उपयोग कर, लेकिन होने सिर्फ सही नहीं लग रहा बारे में सोचा।

तो - कोई विचार? क्या कोई एमवीसी-विशिष्ट चीज है (जैसे एक्शन फ़िल्टर, उदाहरण के लिए) मैं यहां उपयोग कर सकता हूं?

सामान्य सलाह/सुझावों की बहुत सराहना की जाएगी।

+0

'" मैं केवल इस विशेष क्रिया विधि पर कैशिंग करना चाहता हूं "- लगता है जैसे आप एक एक्शनफिल्टर समाधान मांग रहे हैं। – Omar

उत्तर

6

मेरा उत्तर इस धारणा पर आधारित है कि आपकी सेवाएं एक इंटरफेस को लागू करती हैं, उदाहरण के लिए _locationService का प्रकार वास्तव में ILocationService है लेकिन एक ठोस स्थान सेवा के साथ इंजेक्शन दिया जाता है।एक कैशिंगलोकेशन सेवा बनाएं जो ILocationService इंटरफ़ेस लागू करती है और इस कंटेनर को सेवा के उस कैशिंग संस्करण को इंजेक्ट करने के लिए अपने कंटेनर कॉन्फ़िगरेशन को परिवर्तित करती है। कैशिंगलोकेशन सेवा की खुद ही ILocationService पर निर्भरता होगी जिसे मूल स्थान सेवा कक्षा से इंजेक्शन दिया जाएगा। यह वास्तविक व्यापार तर्क को निष्पादित करने के लिए इसका उपयोग करेगा और केवल कैश से खींचने और धक्का देने के साथ ही चिंता करेगा।

आपको मूल स्थान सेवा के समान असेंबली में कैशिंगलोकेशन सेवा बनाने की आवश्यकता नहीं है। यह आपकी वेब असेंबली में हो सकता है। हालांकि, व्यक्तिगत रूप से मैं इसे मूल असेंबली में डालूंगा और नया संदर्भ जोड़ूंगा।

HttpContext पर निर्भरता जोड़ने के लिए; आप

Func<HttpContextBase> 

पर निर्भरता ले रहे हैं और तरह

() => HttpContext.Current 
फिर अपने परीक्षण में

आप HttpContextBase नकली कर सकते हैं कुछ के साथ क्रम में इस इंजेक्शन लगाने के द्वारा इस निकाल सकते हैं, लेकिन आप मुसीबत के बिना कैश वस्तु मजाक हो सकता है TypeMock जैसे कुछ का उपयोग कर।


संपादित करें: आगे .NET 4 System.Runtime.Caching नाम स्थान पर पढ़ने पर, अपने CachingLocationService ObjectCache पर निर्भरता लेना चाहिए। कैश कार्यान्वयन के लिए यह सार आधार वर्ग है। इसके बाद आप सिस्टम के साथ इंजेक्ट कर सकते हैं। Runtime.Caching.MemoryCache.Default, उदाहरण के लिए।

+0

दिलचस्प। और हां आपका अधिकार, मैं 'आईएलोकेशन सेवा' का उपयोग कर रहा हूं, जिसमें 'लोकेशन सेवा' DI के माध्यम से इंजेक्शन दी गई है। 'HttpContext' (और यहां तक ​​कि 'HttpContextBase') के साथ समस्या यह है कि वे' System.Web' असेंबली में मौजूद हैं। इसलिए मेरी "सेवाओं" परियोजना को इसके संदर्भ की आवश्यकता होगी, जो कि (आदर्श) से बचने की कोशिश कर रहा है। आपके उत्तर के लिए धन्यवाद। मुझे यहां कुछ जवाब मिल गए हैं - मुझे उन्हें पचाने के लिए कुछ समय चाहिए। :) – RPM1984

4

ऐसा लगता है कि आप अपने डेटाबेस से प्राप्त डेटा को कैश करने का प्रयास कर रहे हैं।

/// <summary> 
    /// remove a cached object from the HttpRuntime.Cache 
    /// </summary> 
    public static void RemoveCachedObject(string key) 
    { 
     HttpRuntime.Cache.Remove(key); 
    } 

    /// <summary> 
    /// retrieve an object from the HttpRuntime.Cache 
    /// </summary> 
    public static object GetCachedObject(string key) 
    { 
     return HttpRuntime.Cache[key]; 
    } 

    /// <summary> 
    /// add an object to the HttpRuntime.Cache with an absolute expiration time 
    /// </summary> 
    public static void SetCachedObject(string key, object o, int durationSecs) 
    { 
     HttpRuntime.Cache.Add(
      key, 
      o, 
      null, 
      DateTime.Now.AddSeconds(durationSecs), 
      Cache.NoSlidingExpiration, 
      CacheItemPriority.High, 
      null); 
    } 

    /// <summary> 
    /// add an object to the HttpRuntime.Cache with a sliding expiration time. sliding means the expiration timer is reset each time the object is accessed, so it expires 20 minutes, for example, after it is last accessed. 
    /// </summary> 
    public static void SetCachedObjectSliding(string key, object o, int slidingSecs) 
    { 
     HttpRuntime.Cache.Add(
      key, 
      o, 
      null, 
      Cache.NoAbsoluteExpiration, 
      new TimeSpan(0, 0, slidingSecs), 
      CacheItemPriority.High, 
      null); 
    } 

    /// <summary> 
    /// add a non-removable, non-expiring object to the HttpRuntime.Cache 
    /// </summary> 
    public static void SetCachedObjectPermanent(string key, object o) 
    { 
     HttpRuntime.Cache.Remove(key); 
     HttpRuntime.Cache.Add(
      key, 
      o, 
      null, 
      Cache.NoAbsoluteExpiration, 
      Cache.NoSlidingExpiration, 
      CacheItemPriority.NotRemovable, 
      null); 
    } 

मैं Current.cs नाम के एक स्थिर कक्षा में उन तरीकों है: यहाँ कैसे मैं इस (एक दृष्टिकोण है कि मैं कई खुला स्रोत MVC परियोजनाओं में इस्तेमाल देखा है) संभाल है। यहाँ कैसे आप अपने नियंत्रक कार्रवाई करने के लिए उन तरीकों को लागू कर सकते है:

public PartialViewResult GetLocationStuff(SearchPreferences searchPreferences) 
{ 
    var prefs = (object)searchPreferences; 
    var cachedObject = Current.GetCachedObject(prefs); // check cache 
    if(cachedObject != null) return PartialView("SearchResults", cachedObject); 

    var results = _locationService.FindStuffByCriteria(searchPreferences); 
    Current.SetCachedObject(prefs, results, 60); // add to cache for 60 seconds 

    return PartialView("SearchResults", results); 
} 
+0

Current.GetCachedObject (prefs) कैसे काम करता है? यह विधि कैश कुंजी लेती है, कैश्ड ऑब्जेक्ट नहीं। – mikesigs

+2

@ मैक्सिम, और आप इस कोड को प्रत्येक क्रिया में लिखते हैं जिसे कैशिंग की आवश्यकता होती है? –

+0

@whatispunk मेरे उदाहरण में, SearchPreferences कुंजी के रूप में उपयोग किया जाता है, और परिणाम कैश्ड ऑब्जेक्ट होते हैं।कोड पुन: उपयोग के मामले में –

7

मैं सामान्य सलाह प्रदान करेगा और उम्मीद है कि वे सही दिशा बताएगा।

  1. यदि यह आपके एप्लिकेशन में कैशिंग में आपका पहला स्टैब है, तो HTTP प्रतिक्रिया कैश न करें, इसके बजाय एप्लिकेशन डेटा कैश करें। आमतौर पर, आप डेटा कैशिंग के साथ शुरू करते हैं और अपने डेटाबेस को कुछ श्वास कक्ष देते हैं; फिर, यदि यह पर्याप्त नहीं है और आपके ऐप/वेब सर्वर भारी तनाव में हैं, तो आप HTTP प्रतिक्रियाओं को कैशिंग करने के बारे में सोच सकते हैं।

  2. सभी डेटा प्रभावों के साथ एमवीसी प्रतिमान में एक और मॉडल के रूप में अपने डेटा कैश परत का इलाज करें।

  3. जो कुछ भी आप करते हैं, अपना खुद का कैश न लिखें। यह वास्तव में वास्तव में आसान लगता है। Memcached की तरह कुछ का प्रयोग करें।

+0

आपकी युक्तियों के लिए धन्यवाद। सभी मान्य अंक जिन्हें मैं पालन करना चाहता हूं। – RPM1984

25

एक क्रिया विशेषता इसे प्राप्त करने के लिए एक अच्छा तरीका प्रतीत होता है। यहाँ एक उदाहरण है (अस्वीकरण: मैं अपने सिर के ऊपर से लिख रहा हूँ: मैं जब लेखन यह इतना यकीन है कि आप इसे बड़े पैमाने पर परीक्षण :-) बनाने बियर की एक निश्चित मात्रा का सेवन किया है):

public class CacheModelAttribute : ActionFilterAttribute 
{ 
    private readonly string[] _paramNames; 
    public CacheModelAttribute(params string[] paramNames) 
    { 
     // The request parameter names that will be used 
     // to constitute the cache key. 
     _paramNames = paramNames; 
    } 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 
     var cache = filterContext.HttpContext.Cache; 
     var model = cache[GetCacheKey(filterContext.HttpContext)]; 
     if (model != null) 
     { 
      // If the cache contains a model, fetch this model 
      // from the cache and short-circuit the execution of the action 
      // to avoid hitting the repository 
      var result = new ViewResult 
      { 
       ViewData = new ViewDataDictionary(model) 
      }; 
      filterContext.Result = result; 
     } 
    } 

    public override void OnResultExecuted(ResultExecutedContext filterContext) 
    { 
     base.OnResultExecuted(filterContext); 
     var result = filterContext.Result as ViewResultBase; 
     var cacheKey = GetCacheKey(filterContext.HttpContext); 
     var cache = filterContext.HttpContext.Cache; 
     if (result != null && result.Model != null && cache[key] == null) 
     { 
      // If the action returned some model, 
      // store this model into the cache 
      cache[key] = result.Model; 
     } 
    } 

    private string GetCacheKey(HttpContextBase context) 
    { 
     // Use the request values of the parameter names passed 
     // in the attribute to calculate the cache key. 
     // This function could be adapted based on the requirements. 
     return string.Join(
      "_", 
      (_paramNames ?? Enumerable.Empty<string>()) 
       .Select(pn => (context.Request[pn] ?? string.Empty).ToString()) 
       .ToArray() 
     ); 
    } 
} 

और फिर अपने नियंत्रक कार्रवाई ऐसा दिखाई दे सकता:

[CacheModel("id", "name")] 
public PartialViewResult GetLocationStuff(SearchPreferences searchPreferences) 
{ 
    var results = _locationService.FindStuffByCriteria(searchPreferences); 
    return View(results); 
} 

और जहाँ तक सेवा परत में System.Web विधानसभा संदर्भित के साथ आपकी समस्या का संबंध है, जो अब .NET 4.0 में एक समस्या है। एक पूरी तरह से नई असेंबली है जो एक्स्टेंसिबल कैशिंग विशेषताएं प्रदान करती है: System.Runtime.Caching, ताकि आप इसका उपयोग सीधे अपनी सेवा परत में कैशिंग लागू करने के लिए कर सकें।

या इससे भी बेहतर यदि आप अपनी सेवा परत पर ओआरएम का उपयोग कर रहे हैं तो शायद यह ओआरएम कैशिंग क्षमताओं को प्रदान करता है? मुझे उम्मीद है कि यह करता है। उदाहरण के लिए NHibernate second level cache प्रदान करता है।

+3

बहुत बढ़िया। बस कमाल (हमेशा के रूप में)। हाँ मैंने 'System.Runtime.Caching' के बारे में सुना - यह नहीं पता था कि यह एक नई/.NET 4 चीज थी। ओआरएम के लिए - यह बाद में रिपोजिटरी में है (भंडार पर सेवाएं कॉल विधियां), और मैं ईएफ 4 का उपयोग कर रहा हूं। मैं "बुनियादी वस्तुओं" को कैश नहीं करना चाहता, मैं कस्टम ऑब्जेक्ट्स को कैश कर रहा हूं। लेकिन एक्शन फ़िल्टर एक अच्छा विचार जैसा लगता है (अभी तक यह नहीं पता था कि इसे अभी तक कैसे कार्यान्वित किया जाए) - मेरे पास एक खेल होगा ... – RPM1984

+0

हाहाहाहा @ डिस्लेमर संपादित करें। दोस्त को पीओ ... आप इसके लायक हैं, चिंता न करें - कोई भी आपको एमवीसी "शीर्ष उपयोगकर्ताओं" सूची के शीर्ष से डेथ्रोन करने जा रहा है। :) – RPM1984

+0

इसके लिए एक एक्शनफिल्टर का उत्कृष्ट उपयोग। – adammokan

4

मैंने @ जोश के जवाब को स्वीकार कर लिया है, लेकिन सोचा कि मैं अपना जवाब जोड़ूंगा, क्योंकि मैंने बिल्कुल नहीं सुझाया था (करीबी), इसलिए पूर्णता के लिए सोचा कि मैं वास्तव में जो जोड़ूं किया।

कुंजी अब मैं System.Runtime.Caching का उपयोग कर रहा हूं। चूंकि यह एक असेंबली में मौजूद है जो .NET विशिष्ट है और एएसपी.NET विशिष्ट नहीं है, मुझे मेरी सेवा में इसका संदर्भ देने में कोई समस्या नहीं है।

तो मैंने जो कुछ किया है, उसे कैशिंग की आवश्यकता वाले विशिष्ट सेवा परत विधियों में कैशिंग तर्क डाल दिया गया है।

और एक महत्वपूर्ण बात, मैं System.Runtime.Caching.ObjectCache कक्षा से काम कर रहा हूं - यह सेवा के निर्माता में इंजेक्शन दिया गया है।

मेरा वर्तमान DI System.Runtime.Caching.MemoryCache ऑब्जेक्ट इंजेक्ट करता है। ObjectCache कक्षा के बारे में अच्छी बात यह है कि यह सार है और सभी मूल विधियां आभासी हैं।

मेरे यूनिट परीक्षणों के लिए कौन सा मतलब है, मैंने MockCache कक्षा बनाई है, सभी विधियों को ओवरराइड किया है और अंतर्निहित कैश तंत्र को सरल Dictionary<TKey,TValue> के साथ कार्यान्वित किया है।

हम वेग पर स्विच करने की योजना बना रहे हैं - तो फिर, मुझे बस इतना करना है कि ObjectCache कक्षा प्राप्त करना और मुझे जाना अच्छा लगेगा।

सभी की मदद के लिए धन्यवाद!

संबंधित मुद्दे