2012-05-07 25 views
8

में अपने प्रकारों पर एक अनुमान लागू करना कृपया पूरा प्रश्न पढ़ें। मेरे पास कई बाधाओं के साथ एक अनूठी स्थिति है जिसे मैं हल करना चाहता हूं।एक असेंबली लोड करना और किसी अन्य एपडोमेन

मेरे कोड में मेरे पास एक अभिव्यक्ति वृक्ष है जिसे Predicate<System.Type> पर संकलित किया गया है। मेरा लक्ष्य इसे लॉक किए बिना असेंबली लोड करना है (यह प्रोजेक्ट की आउटपुट असेंबली है, जिसे लगातार पुनर्निर्मित किया जा रहा है), इस प्रकार को अपनी प्रकार की सूची पर लागू करें और परिणामी प्रकार नाम:

// this is what I need: 
return assembly.GetTypes().Where(t => predicate(t)).Select(t => t.FullName); 

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

यहां वह मुश्किल है जहां यह मुश्किल हो जाता है। मुझे कई समस्याएं आ रही हैं:

यदि मैं किसी अन्य एपडोमेन में असेंबली लोड करता हूं और बस इसके सभी प्रकारों की एक सरणी वापस लौटाता हूं, ताकि मैं अपने मुख्य एपडोमेन में जैसे ही प्रकार के रूप में भविष्यवाणी कर सकूं मेरे मुख्य एपडोमेन पर वापस झुका हुआ है मुझे FileNotFoundException मिलता है, यह बताते हुए कि यह असेंबली नहीं मिली है। यह संवेदना बनाता है, क्योंकि असेंबली केवल मेरे द्वारा बनाए गए किसी अन्य ऐपडोमेन में लोड की जा रही है। मुख्य एपडोमेन में इसे लोड करना उद्देश्य को हरा देगा।

यदि वैकल्पिक रूप से, मैं अन्य अनुप्रयोगों में भविष्यवाणी करने की कोशिश करता हूं, इसे लागू करने के लिए और तारों (पूर्ण प्रकार का नाम) की एक सरणी वापस लेता हूं, तो मुझे SerializationException: "Cannot serialize delegates over unmanaged function pointers, dynamic methods or methods outside the delegate creator's assembly." मिलता है, क्योंकि भविष्यवाणी एक गतिशील विधि है (संकलित एक अभिव्यक्ति पेड़ से)।

प्राथमिक appdomain में यह लोड हो रहा है इस समस्याओं में से कोई भी हो सकता है, लेकिन क्योंकि यह जैसे ही विधानसभा बदल (के बाद पुनर्निर्माण) हैं पूरे appdomain उतारने के बिना एक भरी हुई विधानसभा अनलोड करने के लिए, असंभव है, किसी भी प्रयास को एक विधानसभा लोड करने के लिए एक ही नाम के साथ एक अपवाद होगा।

प्रसंग:
मैं ReSharper के लिए एक प्लगइन का निर्माण कर रहा हूँ Agent Mulder कहा जाता है। प्लगइन के पीछे विचार आपके समाधान में डीआई/आईओसी कंटेनर पंजीकरण का विश्लेषण करना है, और डीएस कंटेनर के माध्यम से पंजीकृत प्रकारों के उपयोग को रीशेर्पर को समझने में मदद करें (आप here कैसे काम करते हैं इसका संक्षिप्त वीडियो देख सकते हैं)।

अधिकांश भाग के लिए, कंटेनर पंजीकरण का विश्लेषण सरल है - मुझे केवल यह जानने के लिए पर्याप्त जानकारी का पता लगाना है कि कौन से ठोस प्रकार प्रभावित होते हैं। कैसल विंडसर के साथ इस उदाहरण में: Component.For<IFoo>().ImplementedBy<Foo>() परिणामी प्रकार स्पष्ट है, इसलिए AllTypes.FromThisAssembly().BasedOn<IFoo>() है - मुझे इस लाइन से प्रभावित ठोस प्रकारों को अतिथि बनाने के लिए पर्याप्त जानकारी दे रही है।

container.Register(Classes 
    .FromAssemblyInDirectory(new AssemblyFilter(".").FilterByName(an => an.Name.StartsWith("Ploeh.Samples.Booking"))) 
    .Where(t => !(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dispatcher<>))) 
    .WithServiceAllInterfaces()); 

(source)

यहाँ जानकारी एक विधेय है कि केवल रनटाइम पर मूल्यांकन किया जाएगा पर निर्भर करता है: हालांकि, इस पंजीकरण कैसल विंडसर में विचार करें।

चूंकि मैं बस इतना कर सकता हूं कि यह सांख्यिकीय रूप से विश्लेषण कर रहा है, मेरे पास मेरे हाथ में रीशेर्पर एएसटी (जिसे रीशेर्पर में पीएसआई कहा जाता है) Where खंड से लैम्ब्डा अभिव्यक्ति का प्रतिनिधित्व करता है। मैं इस एएसटी को एक LINQ अभिव्यक्ति पेड़ में परिवर्तित कर सकता हूं, फिर इसे एक प्रतिनिधि में संकलित कर सकता हूं।

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

निष्कर्ष में, जब तक कोई भविष्यवाणी से प्रभावित प्रकारों को निर्धारित करने के बेहतर तरीके की सिफारिश नहीं कर सकता, मैं जानना चाहता हूं कि प्रतिबिंब के साथ इसे कैसे किया जाए (दुर्भाग्य से मैंने अन्य मेटाडाटा पाठकों को नहीं माना था, क्योंकि मैं चाहता था किसी भी तरह से लैम्ब्डा अभिव्यक्ति एएसटी को विभिन्न डेटा प्रकार के पूर्वानुमान के रूप में परिवर्तित करना है, और मुझे नहीं पता कि 1-से-1 रूपांतरण मौजूद है या नहीं)।

पढ़ने के लिए धन्यवाद। जब यह उपलब्ध हो जाए तो इस प्रश्न में 500 प्वाइंट बक्षीस होगा।

+0

तो 'Expression' serializable था, तो आप के रूप में' Predicate' प्रतिनिधि बना सकते हैं:

class AssemblyExplorer : MarshalByRefObject { public string[] GetTypesByPredicate( string assemblyPath, RemotablePredicate<Type> predicate) { // MS reflection api reqires all dependencies here var bytes = File.ReadAllBytes(assemblyPath); var assembly = Assembly.ReflectionOnlyLoad(bytes); var types = new List<string>(); foreach (var type in assembly.GetTypes()) if (predicate.Accepts(type)) types.Add(type.FullName); return types.ToArray(); } } 

यह सब काम करें:, लोड विधानसभा का पता लगाने और मुख्य डोमेन के लिए परिणाम देने के कुछ प्रकार का निर्माण एक 'अभिव्यक्ति', और एपडोमेन सीमा से गुजरती है, और इसे 'रिमोट' एपडोमेन में संकलित करती है। लेकिन अफसोस की बात यह है कि यह मामला नहीं है :( – leppie

+0

@leppie हां, मैंने अभिव्यक्ति को क्रमबद्ध करने और इसे पारित करने के बारे में सोचा था, आप सही हैं, यह उतना आसान नहीं है जितना लगता है ... –

उत्तर

2

आपको Type उदाहरण प्राप्त करने के लिए असेंबली लोड करने की आवश्यकता है, इसलिए एक अलग AppDomain सही समाधान की तरह लगता है।

तो, आपको Expression को AppDomain में अनुमानित करने की आवश्यकता है, जिसका अर्थ है कि आपको इसे क्रमबद्ध/deserialize करना है।

यह आवश्यकता विभिन्न कारणों से अधिक से अधिक बार हो रही है। मैं इसे देख रहा था क्योंकि मैं डब्लूसीएफ सेवा में लिंक्स को एंटिटीज एक्सप्रेशन में भेजना चाहता था।

सौभाग्य से, कुछ मौजूदा कार्यान्वयन हैं।

मैं इस एक पाया: CodePlex - Expression Tree Serializer


मैं सिर्फ Types साथ यह परीक्षण किया है, और यह काम करता है:

Expression<Func<Type,bool>> predicate = 
    t => (!t.IsGenericType && t.Name == "Int32"); 

var s = new ExpressionSerialization.ExpressionSerializer(); 
var xml = s.Serialize(predicate); 

var p = s.Deserialize(xml) as Expression<Func<Type, bool>>; 
var f = p.Compile(); 

Console.WriteLine("Int32: " + f(typeof(int))); // true 
Console.WriteLine("String: " + f(typeof(string))); // false 
+0

धन्यवाद! अतिरिक्त एक्रोबेटिक्स (XElement के बाद) स्वयं को '[सीरियलज़ेबल]' चिह्नित नहीं किया गया है, और यह एपडोमेन के बीच स्थानांतरण को तोड़ता है), मैंने स्ट्रिंग में/से कनवर्ट करके इसे चारों ओर काम किया। सरल अभिव्यक्तियों के लिए यह * लगता है * नौकरी करने के लिए - मुझे कोशिश करनी होगी अधिक जटिल वाले। यदि यह काम करता है, तो आपका उत्तर यह हो सकता है :) –

+0

आपका स्वागत है। उस पुस्तकालय ने मुझे बहुत काम बचाया। कृपया मुझे बताएं अगर आपको कोई ऐसा केस मिल जाए जो इसे संभाल नहीं लेता है। –

1

एमबीआर वस्तु, प्रकार Type के पैरामीटर के साथ रैप विधेय प्रतिनिधि उदाहरण चलें अन्य डोमेन से ठीक से मार्शल किया जाएगा:

class RemotablePredicate<T> : MarshalByRefObject 
{ 
    readonly Predicate<T> predicate; 
    public RemotablePredicate(Predicate<T> predicate) { this.predicate = predicate; } 
    public bool Accepts(T arg) { return predicate(arg); } 
} 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq.Expressions; 
using System.Reflection; 

class Program 
{ 
    static void Main() 
    { 
    var fooDomain = AppDomain.CreateDomain("Foo"); 

    Expression<Predicate<Type>> expr = t => t.IsValueType; 
    var compiledPredicate = expr.Compile(); 
    var remotablePredicate = new RemotablePredicate<Type>(compiledPredicate); 

    var explorerType = typeof(AssemblyExplorer); 
    var explorerInstance = (AssemblyExplorer) fooDomain 
     .CreateInstanceAndUnwrap(explorerType.Assembly.FullName, explorerType.FullName); 

    var types = explorerInstance.GetTypesByPredicate(
     "JetBrains.Annotations.dll", remotablePredicate); 

    Console.WriteLine("Matched types: {0}", types.Length); 
    foreach (var type in types) Console.WriteLine(" {0}", type); 
    } 
}