2010-03-05 14 views
60

Moje projekty są ustawione tak:Czy istnieje sposób na wymuszenie załadowania wszystkich przywoływanych zestawów do domeny aplikacji?

  • Project "Definicja"
  • Projekt "Wdrożenie"
  • Project "Konsument"

projektowe odniesienia "konsument" zarówno "Definition" i "Implementacja", ale nie odnosi się statycznie do żadnego typu w "Implementacji".

Po uruchomieniu aplikacji, Project „Konsument” wywołuje statyczną metodę w „Definicja”, który musi znaleźć typy w „realizacji”

Czy istnieje sposób mogę zmusić żadnego odwołuje zespół do być załadowany do Domena aplikacji bez znajomości ścieżki lub nazwy, a najlepiej bez użycia pełnowartościowej architektury IOC?

+1

Jaki problem powoduje? Dlaczego musisz wymusić ładowanie? –

+0

Nie ładuje się w ogóle, prawdopodobnie dlatego, że nie ma zależności statycznej. –

+0

W jaki sposób próbujesz "znaleźć typy" w implementacji? Szukasz czegoś, co implementuje określony interfejs? –

Odpowiedz

68

To wydawało się rade:

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

Jak zauważył Jon, idealnym rozwiązaniem byłoby potrzeba aby powrócić do zależności dla każdego z załadowanych zespołów, ale w moim konkretnym scenariuszu nie muszę się o to martwić.


Aktualizacja: Managed Extensibility Framework (System.ComponentModel) zawarte w .NET 4 ma znacznie lepsze zaplecze dla realizacji takich rzeczy.

+3

To nie działa dla mnie, moje odwołania zespołów, który isnt załadowany, nie pojawia się w AppDomain.CurrentDomain.GetAssemblies() .. Hmm ... – Ted

+8

Jakie udogodnienia? Nic nie znalazłem poprzez wyszukiwanie. – Nuzzolilo

+3

Używając MEF, powyższy kod można skrócić do: 'new DirectoryCatalog (". ");' (Wymaga odwołania do 'System.ComponentModel.Composition'). –

47

Możesz użyć Assembly.GetReferencedAssemblies, aby uzyskać AssemblyName[], a następnie wywołać Assembly.Load(AssemblyName) na każdym z nich. Musisz recurse, oczywiście - ale najlepiej śledzenie zespołów już załadowany :)

+0

Znalazłem to, ale problem polega na tym, że muszę zrobić wszystko, co robię z zespołu referencyjnego ...i przynajmniej w kontekście testu jednostkowego, GetCallingAssembly, GetExecutingAssembly oczywiście zwrócą zestaw referencyjny, a GetEntryAssembly zwróci wartość null: \ –

+4

Jeśli po załadowaniu zestawów referencyjnych powyższe rozwiązanie rozwiąże problem. Możesz również poprosić o określony typ typeof (T) .Assembly, jeśli to pomaga. Mam wrażenie, że potrzebne jest dynamiczne ładowanie zespołów zawierających implementację (bez odniesienia). W takim przypadku musisz albo zachować statyczną listę nazwisk i załadować je ręcznie, albo przechodząc przez cały katalog, załadować, a następnie znaleźć typ z odpowiednimi interfejsami. –

+0

Wydaje się dziwne, że tego rodzaju funkcjonalność nie jest wbudowana w BCL - ten wpis jest raczej stary, czy ktoś jest świadomy tego, że od tego czasu jest dodawany do frameworka? – mfeineis

10

Jeśli użyjesz Fody.Costura lub innego rozwiązania do łączenia zespołów, zaakceptowana odpowiedź nie będzie działać.

Następujące ładuje zespoły referencyjne dowolnego aktualnie załadowanego zespołu. Rekursja jest pozostawiona tobie.

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))); 
+0

Chcesz wiedzieć, gdzie powinien się znaleźć ten fragment? – Telemat

+1

w twoim boot loaderze/uruchomieniu wyobrażam sobie. –

+0

Mogę się mylić, ale myślę, że możesz po prostu sprawdzić '! Y.IsDynamic' w twoim' .Where' – Felype

12

Chciałam udostępnić przykład rekursywny. Dzwonię metodę LoadReferencedAssembly w moim procedury startowej tak:

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

Jest to metoda rekurencyjna:

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)); 
     } 
    } 
} 
+3

Zastanawiam się, czy odniesienia do zespołu okrągłego mogą spowodować wyrzucenie wyjątków przepełnienia stosu. –

+0

Ronnie, nie wierzę, kod uruchamia tylko rekursję w przypadku, gdy 'name' nie znajduje się już w' AppDomain.CurrentDomain.GetAssemblies() ', co oznacza, że ​​będzie się on powtarzał tylko wtedy, gdy' foreach' wybrany 'AssemblyName' nie jest jeszcze załadowany. – Felype

0

Widząc jak miałem załadować montaż + zależności od konkretnej ścieżki dzisiaj napisał tę klasę, aby to zrobić.

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); 
    } 
} 
Powiązane problemy