क्या सभी संदर्भित विधानसभाओं को ऐप डोमेन में लोड करने के लिए मजबूर करने का एक तरीका है?


83

मेरी परियोजनाएँ इस तरह स्थापित की गई हैं:

  • परियोजना की परिभाषा"
  • परियोजना कार्यान्वयन"
  • परियोजना "उपभोक्ता"

प्रोजेक्ट "उपभोक्ता" "परिभाषा" और "कार्यान्वयन" दोनों का संदर्भ देता है, लेकिन "कार्यान्वयन" में किसी भी प्रकार का संदर्भ नहीं देता है।

जब अनुप्रयोग शुरू होता है, तो प्रोजेक्ट "उपभोक्ता" "परिभाषा" में एक स्थिर विधि कहता है, जिसे "कार्यान्वयन" में प्रकार खोजने की आवश्यकता होती है

क्या कोई रास्ता है जो मैं किसी संदर्भित विधानसभा को पथ या नाम के बिना ऐप डोमेन में लोड करने के लिए मजबूर कर सकता हूं, और अधिमानतः पूर्ण-पूर्ण आईओसी ढांचे का उपयोग किए बिना?


1
यह किस तरह की समस्या पैदा कर रहा है? आपको लोडिंग को मजबूर करने की आवश्यकता क्यों है?
माइक दो

यह बिल्कुल भी लोड नहीं हो रहा है, शायद इसलिए कि कोई स्थिर निर्भरता नहीं है
डैनियल शेफ़र

आप कार्यान्वयन में "प्रकार खोजने" की कोशिश कैसे कर रहे हैं? क्या आप किसी ऐसी चीज की तलाश कर रहे हैं जो एक विशिष्ट इंटरफ़ेस को लागू करती है?
माइक टू

2
@ माइक: हाँ। मैं AppDomain.CurrentDomain.GetAssemblies कर रहा हूं, और उनमें से प्रत्येक पर GetTypes () को कॉल करने के लिए एक linq क्वेरी का उपयोग कर रहा हूं।
डेनियल शॉफर

जवाबों:


90

यह चाल करने के लिए लग रहा था:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray();

var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();

toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));

जैसा कि जॉन ने उल्लेख किया है, आदर्श समाधान को प्रत्येक भरी हुई विधानसभाओं के लिए निर्भरता में पुनरावृत्ति करना होगा, लेकिन मेरे विशिष्ट परिदृश्य में मुझे इसके बारे में चिंता करने की आवश्यकता नहीं है।


अद्यतन: .NET 4 में शामिल प्रबंधित एक्स्टेंसिबिलिटी फ्रेमवर्क (System.ComponentModel) में चीजों को पूरा करने के लिए बेहतर सुविधाएं हैं।


5
यह मेरे लिए काम नहीं करता है, मेरी संदर्भित असेंबली, जो भरी हुई नहीं है, AppDomain.CurrentDomain.GetAssemblies () में प्रदर्शित नहीं होती है .. हम्म ...
टेड

11
क्या सुविधाएं? मुझे खोज के माध्यम से कुछ भी नहीं मिला है।
nuzzolilo

8
MEF का उपयोग करते हुए, उपरोक्त कोड को छोटा किया जा सकता है: new DirectoryCatalog(".");(संदर्भित करने के लिए System.ComponentModel.Composition) की आवश्यकता होती है ।
एलोन गुरिलनेक

1
इस उत्तर ने मेरी समस्या को हल कर दिया और मेरे लिए काम किया। मेरी एक इकाई इकाई परीक्षण परियोजना थी जिसमें मेरी एक और असेंबली और AppDomain.CurrentDomain.GetAssemblies () का उल्लेख किया गया था, जो उस समय नहीं लौट रही थी जब एक परीक्षण चल रहा था। मुझे संदेह है कि भले ही मेरी इकाई परीक्षण उस पुस्तकालय से कोड का उपयोग कर रहे थे, हो सकता है कि असेंबली "GetAssemblies" में नहीं दिख रही हो, क्योंकि एक नियमित रूप से चलाने की तुलना में vs.net एक MS इकाई परीक्षण परियोजना (कक्षा पुस्तकालय) को लोड करता है। .exe आवेदन यदि आपका कोड प्रतिबिंब का उपयोग करता है और आपकी इकाई परीक्षणों को विफल कर रहा है, तो इसे ध्यान में रखें।
डीन लुनज

4
बस जोड़ना चाहते थे, गतिशील रूप से भरी हुई विधानसभाओं से सावधान रहें। आह्वान किया गया सदस्य एक गतिशील विधानसभा में समर्थित नहीं है। या तो असेंबलियों को फ़िल्टर करें जहाँ IsDynamic = false, या यदि आप भार के सहिष्णु हो सकते हैं, तो CurrentDomain.Load पर अपने कॉल को आज़माएं / पकड़ें। और assembly.Location। उस एक को भी जांचने की जरूरत है। IsDynamicविधानसभाओं के लिए काम नहीं करता है ।
एली गैसर्ट

63

आप Assembly.GetReferencedAssembliesएक पाने के लिए उपयोग कर सकते हैं AssemblyName[], और फिर Assembly.Load(AssemblyName)उनमें से प्रत्येक पर कॉल कर सकते हैं। आपको निश्चित रूप से पुनरावृत्ति करने की आवश्यकता होगी - लेकिन अधिमानतः उन विधानसभाओं पर नज़र रखना जिन्हें आपने पहले ही लोड किया है :)


मैंने पाया कि, लेकिन समस्या यह है कि मुझे संदर्भित विधानसभा से जो कुछ भी करना है, वह करना है ... और कम से कम एक इकाई परीक्षण के संदर्भ में, GetCallingAssembly, GetExecutingAssembly संदर्भ विधानसभा को लौटाएं, और GetEntryAssembly रिटर्न शून्य : \ _
डैनियल Schaffer

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

1
@vanhelgen: यह शायद ही कभी कुछ है जो आपको स्पष्ट रूप से मेरे अनुभव में चाहिए। आम तौर पर सीएलआर का "डिमांड पर लोड" ठीक काम करता है।
जॉन स्कीट

2
सामान्य परिस्थितियों में, जो सच हो सकता है लेकिन उपलब्ध सेवाओं की खोज के लिए DI कंटेनर का उपयोग करते समय ( System.Reflectionस्वाभाविक रूप से ) यह स्वाभाविक रूप से उन विधानसभाओं में निहित सेवाओं को नहीं खोजता है जो अभी तक लोड नहीं किए गए हैं। जब से सभी निर्भरताएं हैं, यह सुनिश्चित करने के लिए मेरे ऐप के कंपोज़रूट में प्रत्येक संदर्भित विधानसभा के यादृच्छिक प्रकार से एक डमी उप वर्ग बनाने के लिए मेरा डिफ़ॉल्ट दृष्टिकोण तब से है। मुझे आशा है कि मैं आगे बढ़ते स्टार्टअप समय की कीमत पर भी सब कुछ अप लोड करके इस बकवास को छोड़ सकता हूं। @JonSkeet ऐसा करने का एक और तरीका है? thx
mfeineis

12
यह नीचे गिर जाता है कि GetReferencedAssemblies स्पष्ट रूप से एक "अनुकूलित" सूची देता है - इसलिए यदि आप स्पष्ट रूप से संदर्भित विधानसभा में कोड नहीं कहते हैं, तो यह शामिल नहीं होगा। ( इस चर्चा के अनुसार )
FTWinston

23

बस एक पुनरावर्ती उदाहरण साझा करना चाहता था। मैं अपने स्टार्टअप रूटीन में LoadReferencedAssembly विधि को इस तरह बुला रहा हूं:

foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    this.LoadReferencedAssembly(assembly);
}

यह पुनरावर्ती विधि है:

private void LoadReferencedAssembly(Assembly assembly)
{
    foreach (AssemblyName name in assembly.GetReferencedAssemblies())
    {
        if (!AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName == name.FullName))
        {
            this.LoadReferencedAssembly(Assembly.Load(name));
        }
    }
}

5
मुझे आश्चर्य है कि अगर सर्कुलर असेंबली रेफरेंस से स्टैक ओवरफ्लो अपवाद हो सकता है।
रॉनी ओवरबाई

1
रॉनी, मुझे विश्वास है कि नहीं, कोड केवल पुनरावृत्ति चलाता है मामले nameमें पहले से ही नहीं है AppDomain.CurrentDomain.GetAssemblies(), जिसका अर्थ है कि यह केवल पुनरावृत्ति करेगा यदि foreachउठाया AssemblyNameअभी तक लोड नहीं किया गया है।
फुलेल

1
मैं O(n^2)इस एल्गोरिथ्म के रनटाइम ( GetAssemblies().Any(...)अंदर foreach) के बारे में खुश नहीं हूं । मैं HashSetके आदेश पर कुछ के लिए यह नीचे लाने के लिए एक का उपयोग करेंगे O(n)
दाई

16

यदि आप Fody.Costura, या किसी अन्य असेंबली मर्जिंग समाधान का उपयोग करते हैं, तो स्वीकृत उत्तर काम नहीं करेगा।

निम्नलिखित किसी भी वर्तमान में भरी हुई विधानसभा की संदर्भित विधानसभाओं को लोड करता है। पुनरावृत्ति आप के लिए छोड़ दिया है।

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();

loadedAssemblies
    .SelectMany(x => x.GetReferencedAssemblies())
    .Distinct()
    .Where(y => loadedAssemblies.Any((a) => a.FullName == y.FullName) == false)
    .ToList()
    .ForEach(x => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(x)));

सलाह देने के लिए कि यह स्निपेट कहाँ जाना चाहिए?
टेलीमैट

1
अपने बूट लोडर / स्टार्ट-अप में मैं कल्पना करता हूं।
मीरियन ह्यूजेस

1
मैं गलत हो सकता हूं, लेकिन मुझे लगता है कि आप बस !y.IsDynamicअपने लिए जाँच कर सकते हैं.Where
Felype

1

यह देखकर कि मुझे आज एक विशिष्ट पथ से एक विधानसभा + निर्भरता लोड करनी थी, मैंने इसे करने के लिए यह वर्ग लिखा था।

public static class AssemblyLoader
{
    private static readonly ConcurrentDictionary<string, bool> AssemblyDirectories = new ConcurrentDictionary<string, bool>();

    static AssemblyLoader()
    {
        AssemblyDirectories[GetExecutingAssemblyDirectory()] = true;
        AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;

    }

    public static Assembly LoadWithDependencies(string assemblyPath)
    {
        AssemblyDirectories[Path.GetDirectoryName(assemblyPath)] = true;
        return Assembly.LoadFile(assemblyPath);
    }

    private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
    {
        string dependentAssemblyName = args.Name.Split(',')[0] + ".dll";
        List<string> directoriesToScan = AssemblyDirectories.Keys.ToList();

        foreach (string directoryToScan in directoriesToScan)
        {
            string dependentAssemblyPath = Path.Combine(directoryToScan, dependentAssemblyName);
            if (File.Exists(dependentAssemblyPath))
                return LoadWithDependencies(dependentAssemblyPath);
        }
        return null;
    }

    private static string GetExecutingAssemblyDirectory()
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        var uri = new UriBuilder(codeBase);
        string path = Uri.UnescapeDataString(uri.Path);
        return Path.GetDirectoryName(path);
    }
}

1
अच्छे कोड को छोड़कर, शब्दकोश की कोई आवश्यकता नहीं है, इस मामले में एक साधारण सूची पर्याप्त होगी। मैं मान रहा हूं कि आपके मूल कोड को यह जानने की जरूरत है कि कौन सी विधानसभाएं भरी हुई थीं और कौन सी नहीं थीं, यही वजह है कि आपके पास डिक्शनरी है।
Reinis

0

फिर भी एक और संस्करण ( डैनियल शेफ़र जवाब पर आधारित ) मामला है जब आपको सभी विधानसभाओं को लोड करने की आवश्यकता नहीं हो सकती है, लेकिन उनमें से एक पूर्वनिर्धारित संख्या:

var assembliesToLoad = { "MY_SLN.PROJECT_1", "MY_SLN.PROJECT_2" };

// First trying to get all in above list, however this might not 
// load all of them, because CLR will exclude the ones 
// which are not used in the code
List<Assembly> dataAssembliesNames =
   AppDomain.CurrentDomain.GetAssemblies()
            .Where(assembly => AssembliesToLoad.Any(a => assembly.GetName().Name == a))
            .ToList();

var loadedPaths = dataAssembliesNames.Select(a => a.Location).ToArray();

var compareConfig = StringComparison.InvariantCultureIgnoreCase;
var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
    .Where(f =>
    {
       // filtering the ones which are in above list
       var lastIndexOf = f.LastIndexOf("\\", compareConfig);
       var dllIndex = f.LastIndexOf(".dll", compareConfig);

       if (-1 == lastIndexOf || -1 == dllIndex)
       {
          return false;
       }

       return AssembliesToLoad.Any(aName => aName == 
          f.Substring(lastIndexOf + 1, dllIndex - lastIndexOf - 1));
     });

var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();

toLoad.ForEach(path => dataAssembliesNames.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));

if (dataAssembliesNames.Count() != AssembliesToLoad.Length)
{
   throw new Exception("Not all assemblies were loaded into the  project!");
}

0

यदि आपके पास ऐसी असेंबलियाँ हैं जहाँ संकलित समय में कोई कोड संदर्भित नहीं है, तो उन असेंबलीज़ को आपकी अन्य असेंबली के संदर्भ के रूप में शामिल नहीं किया जाएगा, भले ही आपने प्रोजेक्ट या नगेट पैकेज को संदर्भ के रूप में जोड़ा हो। यह सेटिंग, कोड ऑप्टिमाइज़ेशन आदि की परवाह किए बिना है Debugया नहीं है Release। इन मामलों में, आपको Assembly.LoadFrom(dllFileName)असेंबली लोड करने के लिए स्पष्ट रूप से कॉल करना होगा ।


0

नाम से संदर्भित विधानसभा प्राप्त करने के लिए आप निम्नलिखित विधि का उपयोग कर सकते हैं:

public static Assembly GetAssemblyByName(string name)
{
    var asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == name);
    if (asm == null)
        asm = AppDomain.CurrentDomain.Load(name);
    return asm;
}

0

मेरे winforms आवेदन में मैं जावास्क्रिप्ट (एक WebView2 नियंत्रण में) विभिन्न .NET चीजों को कॉल करने की संभावना देता हूं, उदाहरण के Microsoft.VisualBasic.Interactionलिए असेंबली Microsoft में तरीके ।VisualBasic.dll (जैसेInputBox() आदि)।

लेकिन मेरा आवेदन जैसे कि उस विधानसभा का उपयोग नहीं करता है, इसलिए विधानसभा कभी भरी हुई नहीं है।

तो विधानसभा को लोड करने के लिए मजबूर करने के लिए, मैंने इसे अपने फॉर्म 1 में जोड़ा।

if (DateTime.Now < new DateTime(1000, 1, 1, 0, 0, 0)) { // never happens
  Microsoft.VisualBasic.Interaction.Beep();
  // you can add more things here
}

संकलक सोचता है कि विधानसभा की आवश्यकता हो सकती है, लेकिन वास्तव में यह कभी नहीं होता है।

बहुत परिष्कृत समाधान नहीं है, लेकिन त्वरित और गंदा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.