2012-04-24 19 views
8

मैं एक .NET आधारित अनुप्रयोग बना रहा हूं, और एक अधिक विस्तारणीय और प्लग करने योग्य डिज़ाइन की अनुमति देना चाहता हूं।अनुमोदित सी # प्लगइन्स आवेदन हुक पर पंजीकरण

सादगी के लिए, आवेदन के संचालन और घटनाओं का एक सेट को उजागर करता है:

  • DoSomething()
  • DoOtherThing()
  • OnError
  • onSuccess

मैं लोड होने के लिए "प्लगइन्स" पेश करना और इन परिचालनों में से कुछ में हुक करना चाहते हैं (कुछ ऐसा है: जब ईवेंट 1 आग लगती है, प्लगइन 1 चलाएं)।

उदाहरण के लिए - plugin1.HandleError() जब OnError घटना आग चलाते हैं।

इस घटना को सदस्यता के साथ आसानी से किया जा सकता है:

this.OnError += new Plugin1().HandleError(); 

समस्या यह है कि यह है:

  1. मेरा ऐप प्रकार "Plugin1" का पता नहीं है (यह एक प्लगइन है, मेरे ऐप सीधे इसका संदर्भ नहीं देता है)।
  2. ऐसा करने से पहले प्लगइन को तुरंत चालू कर दिया जाएगा, कुछ मैं नहीं करना चाहता हूं।

"पारंपरिक" प्लगइन मॉडल में, एप्लिकेशन (प्लगइन का "क्लाइंट" लोड करता है और कुछ महत्वपूर्ण बिंदुओं पर प्लगइन कोड निष्पादित करता है। उदाहरण के लिए - एक निश्चित प्रसंस्करण अनुप्रयोग, जब एक निश्चित ऑपरेशन किया जाता है)।

प्लगइन कोड को तुरंत चालू करने के लिए और इसे निष्पादित करने का नियंत्रण क्लाइंट एप्लिकेशन के लिए जाना जाता है।

मेरे आवेदन में, प्लगइन स्वयं यह तय करना है कि इसे कब निष्पादित करना चाहिए ("प्लगइन ऑनरर ईवेंट पर पंजीकृत होना चाहिए")।

"पंजीकरण" कोड के साथ प्लगइन "निष्पादन" कोड को रखना एक मुद्दा है कि प्लगइन डीएलएल के साथ पंजीकरण समय पर स्मृति में लोड हो जाएगा, जो कुछ मैं रोकना चाहता हूं।

उदाहरण के लिए, यदि मैं प्लगइन डीएलएल में एक रजिस्टर() विधि जोड़ता हूं, तो प्लगइन डीएलएल को रजिस्टर विधि के लिए स्मृति में लोड करना होगा।

इस विशेष मुद्दे के लिए एक अच्छा डिजाइन समाधान क्या हो सकता है?

  • प्लगइन डीएलएल के आलसी लोडिंग (या आलसी/उत्सुक लोडिंग की पेशकश)।
  • प्लगइन को नियंत्रित करने के लिए सिस्टम/ऐप के विभिन्न हिस्सों को नियंत्रित करने के लिए अनुमति देना।

उत्तर

4

आपको डीएल के पथ को खोजने के लिए क्या करना है, और उसके बाद से एक असेंबली ऑब्जेक्ट बनाएं।

var assembly = Assembly.Load(AssemblyName.GetAssemblyName(fileFullName)); 
foreach (Type t in assembly.GetTypes()) 
{ 
    if (!typeof(IMyPluginInterface).IsAssignableFrom(t)) continue; 
    var instance = Activator.CreateInstance(t) as IMyPluginInterface; 
    //Voila, you have lazy loaded the plugin dll and hooked the plugin class to your code 
} 
बेशक

, यहाँ से आप जो कुछ भी आप चाहते हैं, उपयोग के तरीकों, की सदस्यता करने के लिए स्वतंत्र हैं: वहाँ से, आप कक्षाएं आप पुनः प्राप्त करना चाहते हैं पाने के लिए (उदाहरण के लिए, कुछ भी है कि अपने इंटरफेस को लागू करता है) की आवश्यकता होगी घटनाओं आदि

+1

असेंबली लोड करते समय इस रणनीति की कमी बहुत धीमी है। .Net –

+0

में प्रतिबिंब के लिए यह सामान्य समस्या है धन्यवाद, मुझे पहले से ही यह पता है कि यह कैसे करें। मैं जो डिज़ाइन करना चाहता हूं वह प्लगइन को कुछ घटनाओं पर पंजीकृत करने की अनुमति देने का एक तरीका है, इसे पहले से लोड किए बिना। –

+0

मुझे लगता है कि यह वास्तव में लोड होने तक * कुछ भी * प्लगइन डीएलएल के लिए संभव नहीं है। एक समाधान प्लगइन सेट अप करने वाली प्रत्येक प्लगइन असेंबली में एक प्लगइन परिभाषा वर्ग डालना है/मेजबान को इसकी क्षमताओं के बारे में बताता है। जब आपका एप्लिकेशन शुरू होता है तो आप सभी परिभाषा कक्षाएं पा सकते हैं और उचित कार्रवाई कर सकते हैं (शायद प्रत्येक पर "कॉन्फ़िगर करें" विधि को कॉल करना)। –

2

लोड हो रहा है प्लगइन असेंबलियों मैं एक निर्देशिका (मैं StructureMap का उपयोग करें) से विधानसभाओं लोड करने के लिए मेरी आईओसी कंटेनर पर दुबला करने के लिए हालांकि आप उन्हें @ ऑस्कर के जवाब के अनुसार मैन्युअल रूप से लोड कर सकते हैं करते हैं के लिए।

यदि आप लोडिंग प्लगइन का समर्थन करना चाहते हैं, जबकि आपका एप्लिकेशन चल रहा है, तो स्ट्रक्चर मैप को "पुन: कॉन्फ़िगर किया जा सकता है", इस प्रकार कोई भी नया प्लगइन उठाया जा सकता है।

आपके आवेदन हुक के लिए आप ईवेंट ईवेंट में ईवेंट भेज सकते हैं। उदाहरण के नीचे सभी पंजीकृत ईवेंट हैंडलर्स को खोजने के लिए StructureMap का उपयोग करता है, लेकिन आप सादे पुराने प्रतिबिंब या किसी अन्य आईओसी कंटेनर इस्तेमाल कर सकते हैं:

public interface IEvent { } 
public interface IHandle<TEvent> where TEvent : IEvent { 
    void Handle(TEvent e); 
} 

public static class EventBus { 
    public static void RaiseEvent(TEvent e) where TEvent : IEvent { 
     foreach (var handler in ObjectFactory.GetAllInstances<IHandle<TEvent>>()) 
      handler.Handle(e); 
    } 
} 

फिर आप इतने तरह की एक घटना को बढ़ा सकते हैं:

public class Foo { 
    public Foo() { 
     EventBus.RaiseEvent(new FooCreatedEvent { Created = DateTime.UtcNow }); 
    } 
} 

public class FooCreatedEvent : IEvent { 
    public DateTime Created {get;set;} 
} 

और इसे संभाल (उदाहरण के लिए अपने प्लगइन में) ताकि तरह:

public class FooCreatedEventHandler : IHandle<FooCreatedEvent> { 
    public void Handle(FooCreatedEvent e) { 
     Logger.Log("Foo created on " + e.Created.ToString()); 
    } 
} 

मैं निश्चित रूप से जो विकासशील के साथ मुद्दों के कई शामिल किया गया है pl शैनन Deminick द्वारा this post सिफारिश करेंगे uggable अनुप्रयोगों। यह वही है जो हमने अपने "प्लगइन प्रबंधक" के लिए आधार के रूप में उपयोग किया था।

व्यक्तिगत रूप से मैं मांग पर असेंबली लोड करने से बचूंगा। आईएमओ चलाने वाले उपयोगकर्ताओं की तुलना में आवश्यक प्लगइन्स लोड होने की प्रतीक्षा करने के लिए थोड़ा अधिक समय शुरू करना (वेब ​​एप्लिकेशन पर एक समस्या से भी कम) होना बेहतर है।

+0

दिलचस्प। दोष (मेरे लिए कम से कम) प्रत्येक घटना के लिए एक नई कक्षा बनाने का उपर है, और हर बार जब मैं अपने सिस्टम में एक नई घटना का खुलासा करना चाहता हूं। –

+0

इन हल्के घटनाओं और हैंडलर बनाने के लिए जितना समय लगता है, वह शायद पारंपरिक घटना और हैंडलर को तारने के लिए समान होता है। मैं नहीं देख सकता कि नई घटनाओं को प्रकाशित करने का एक तेज तरीका है। –

+0

इसके अलावा, मैं (प्रकार) .NET 'ईवेंट' को परिभाषित करने की क्षमता खो देता हूं + = उन्हें सदस्यता लेना। ईवेंट प्रेषण एक विधि कॉल का उपयोग करके आपके उदाहरण के अनुसार किया जाता है + कंटेनर को 'सब्स्क्राइब किए गए' वर्गों को हल करने दें। –

5

आप एक गैर-मौजूदा समस्या को हल करने का प्रयास कर रहे हैं। जब आपका कोड असेंबली कॉल करता है तो सभी प्रकार की मानसिक छवि लोड हो जाती है। लोड से() गलत है। .NET Framework विंडोज़ का एक पूर्ण-लाभ प्राप्त करता है जो एक मांग-आधारित वर्चुअल मेमोरी ऑपरेटिंग सिस्टम है। और फिर कुछ।

जब आप LoadFrom() को कॉल करते हैं तो गेंद रोलिंग हो जाती है। सीएलआर एक कोर ऑपरेटिंग सिस्टम अबास्ट्रक्शन, स्मृति मैप की गई फ़ाइल बनाता है। यह एपडोमेन में अब निवासी होने का असेंबली रखने के लिए आंतरिक स्थिति का एक छोटा सा अद्यतन करता है, यह बहुत मामूली है। एमएमएफ अप मेमोरी मैपिंग सेट करता है, जो वर्चुअल मेमोरी पेज बनाता है जो फ़ाइल की सामग्री को मैप करता है। प्रोसेसर के टीएलबी में बस एक छोटा वर्णनकर्ता। वास्तव में असेंबली फ़ाइल से कुछ भी नहीं पढ़ा जाता है।

अगला आप इंटरफ़ेस लागू करने वाले प्रकार को खोजने का प्रयास करने के लिए प्रतिबिंब का उपयोग करेंगे। इससे सीएलआर असेंबली से कुछ असेंबली मेटाडेटा पढ़ता है। इस बिंदु पर, पेज त्रुटियां प्रोसेसर को उन पृष्ठों के सामग्री को मैप करने का कारण बनती हैं जो असेंबली के मेटाडेटा सेक्शन को रैम में कवर करती हैं। कुछ हद तक किलोबाइट्स, संभवतः अधिक यदि असेंबली में कई प्रकार होते हैं।

अगला, केवल समय-समय पर संकलक कन्स्ट्रक्टर के लिए कोड उत्पन्न करने के लिए कार्रवाई में पड़ता है। इससे प्रोसेसर उस पृष्ठ को गलती कर देता है जिसमें कंस्ट्रक्टर आईएल रैम में होता है।

Etcetera। कोर विचार यह है कि असेंबली सामग्री हमेशा आलसी पढ़ती है, केवल तभी जब की आवश्यकता होती है।यह तंत्र प्लगइन के लिए अलग नहीं है, वे आपके समाधान में नियमित असेंबली की तरह काम करते हैं। केवल अंतर के साथ कि आदेश थोड़ा अलग है। आप पहले असेंबली लोड करते हैं, फिर तुरंत निर्माता को कॉल करें। आपके कोड और सीएलआर में किसी प्रकार के कन्स्ट्रक्टर को कॉल करने के विरोध में तुरंत असेंबली लोड हो रही है। इसमें बस इतना समय लगता है।

+0

मेरा प्रश्न यह तय करने की ज़िम्मेदारी को स्थानांतरित करने के बारे में है कि क्लाइंट कोड से प्लगइन को प्लगइन में कब बुलाया जाए। मैं प्लगइन को कैसे कहूं कि कौन सी घटना खुद को लोड करनी चाहिए और निष्पादित करनी चाहिए, बिना खुद को लोड किए? किंडा चिकन और अंडे की स्थिति। –

+1

मुझे लगता है कि हंस क्या कहने की कोशिश कर रहा है, कि आपका रजिस्टरफॉरऑपरेशन 1 हुक-दृष्टिकोण पूरी असेंबली को स्मृति में लोड नहीं करता है, इस प्रकार आपकी समस्या का एक अच्छा समाधान है :) – cwap

+0

मुझे यकीन नहीं है कि मेरे प्लगइन में एक प्रकार का निर्माण कैसे किया जा रहा है। डीएलएल और उस पर एक रजिस्टर() विधि का आह्वान करने से डीएलएल को स्मृति में लोड नहीं किया जाएगा। मेरा उद्देश्य निश्चित रूप से डीएलएल को अनलोड करना है (इसे अपने स्वयं के ऐपडोमेन में लोड किया जाएगा), यह इस समाधान का उपयोग करना संभव नहीं होगा। –