2010-07-30 6 views
1

Dans mon application, je souhaite charger des assemblages résidant sur un partage réseau (l'application elle-même est également sur ce partage) sans avoir à les ajouter au chemin de sortie du projet. Ce que j'aimerais avoir, c'est un répertoire de sortie qui ne contienne que l'application. Ajouter simplement le répertoire cible au chemin de référence de mon projet ne fonctionnera pas.Référencement d'assemblages dans une application .Net

j'essayé de placer le répertoire de sondage dans le App.Config, mais cela ne fonctionne pas (même testé sur mes diks locaux au lieu du partage réseau)

Merci à l'avance pour les conseils!

EDIT: Apparaît la façon dont cela se passe conduit par la réflexion. N'y a-t-il pas une manière plus élégante d'utiliser des conteneurs d'injection de dépendances comme NInject?

+0

Dans la plupart des cas, avoir tous les assemblages dans le chemin de sortie n'est pas si mauvais qu'il semble à première vue. Alors pourquoi pensez-vous que vous devez le faire différemment? –

+0

Fondamentalement, vous avez raison - ne fait pas trop mal de les avoir là. Mais nous avons quelque part près de 12 Frontends qui utilisent une de nos libs dans la version 1.2.x et trois autres qui nécessitent 1.2.y (ou toute autre version, juste en donnant des nombres aléatoires). Donc, ce que nous faisons est d'extraire des interfaces de ces bibliothèques (pour les intégrer dans des projets) et de supprimer les assemblys d'implémentation de l'application (les mettre sur le réseau pour les charger dynamiquement).L'avantage de cette approche est que nous n'aurons même pas à recompiler notre application quand un correctif pour une bibliothèque devient nécessaire - l'interface reste la même. –

+0

Ninject ne vous donne rien que vous ne pouvez pas faire par le code. Pour utiliser Ninject (et d'autres conteneurs DI), vous devez pouvoir charger l'assemblage d'une manière ou d'une autre. –

Répondre

2

Malheureusement, vous ne pouvez pas ajouter un répertoire arbitraire à votre chemin de vérification: seulement un sous-répertoire dans votre application.

Toutefois, il est possible de fournir des chemins d'accès complets aux assemblys à l'aide de l'élément <codeBase> au sein d'un <assemblyBinding>. Il y a un exemple sur le MSDN page for <assemblyBinding>. L'inconvénient est que vous aurez besoin de lister explicitement les chemins pour tous les assemblages référencés par votre application.

Une autre option consiste à charger les assemblages au moment de l'exécution via Assembly.LoadFrom et d'appeler des méthodes par réflexion. Si vous suivez cette route, vous devez lire Suzanne Cook's blog. Si vous ne lisez rien d'autre, lisez Choosing a Binding Context.

2

Je voudrais jeter un coup d'oeil à Assembly.LoadFrom. Vous pouvez écrire du code comme ceci:

Assembly myAssembly = Assembly.LoadFrom(assemblyPath); 

Notez que vous devrez également charger tous les assemblages dépendants. Une fois que vous avez cela, vous pouvez créer comme objet en utilisant:

object myObject = myAssembly.CreateInstance(typeName); 

et de le jeter dans le type spécifié par typeName

0

Si je comprends bien, vous voulez charger les classes d'un ensemble sans avoir à faire référence à la projet d'assemblage?

Ensuite, vous pouvez utiliser cette classe pour obtenir tous les types qui implémente/hérite d'un certain type:

Imports System.IO 
Imports System.Threading 
Imports Exceptions 
Imports System.Reflection 
Imports ModuleInterfaces 

Public Class StartupLoader 
    Private ReadOnly syncroot As New Object 

    Private _handler As ExceptionHandler ' custom class to handle exceptions' 
    Private _typeToLoad As Type 

    Public Sub New(ByVal typeToLoad As Type, _ 
        ByVal handler As ExceptionHandler) 
     _handler = handler 
     _typeToLoad = typeToLoad 
    End Sub 

    Public Function LoadDLLs() As List(Of Type) 
     Dim threads As New List(Of Thread) 
     Dim types As New List(Of Type) 
     Dim exceptions As New List(Of Exception) 
     Dim folders As New Stack(Of String) 
     Dim t As Thread 

     folders.Push(Directory.GetCurrentDirectory) ' change to your dir here, could use a member var' 
     While Not folders.Count = 0 
      For Each f In Directory.GetFiles(folders.Peek) 
       Dim tmp As String = f 
       If tmp.ToLower.EndsWith(".dll") OrElse _ 
        tmp.ToLower.EndsWith(".exe") Then 
        t = New Thread(AddressOf LoadDLLsThread) 

        t.Start(New Object() {tmp, types, exceptions}) 
        threads.Add(t) 
       End If 
      Next 

      For Each d In Directory.GetDirectories(folders.Peek) 
       folders.Push(d) 
      Next 

      folders.Pop() 
     End While 

     For Each t In threads 
      t.Join() 
     Next 

     If exceptions.Count > 0 Then 
      Throw New ThreadedException(exceptions) ' Custom exception containing a List(Of Exception)' 
     End If 

     Return types 

    End Function 

    Private Sub LoadDLLsThread(ByVal vObj As Object) 
     Dim objs As Object() = CType(vObj, Object()) 
     Dim fileName As String = CStr(objs(0)) 
     Dim globalTypes As List(Of Type) = CType(objs(1), Global.System.Collections.Generic.List(Of Global.System.Type)) 
     Dim exceptions As List(Of Exception) = CType(objs(2), Global.System.Collections.Generic.List(Of Global.System.Exception)) 

     Dim types As New List(Of Type) 

     Try 
      Dim myAssembly As Assembly = Assembly.LoadFrom(fileName) 

      For Each t In myAssembly.GetTypes() 
       If _typeToLoad.IsAssignableFrom(t) AndAlso _ 
        t.IsClass = True AndAlso _ 
        t.IsAbstract = False Then 
        types.Add(t) 
       End If 
      Next 

      SyncLock syncroot 
       globalTypes.AddRange(types) 
      End SyncLock 

     Catch ex As Exception 
      SyncLock syncroot 
       exceptions.Add(ex) 
      End SyncLock 
     End Try 

    End Sub 

End Class 

Après cela, utilisez la réponse de John Sibly pour créer un objet de l'un des types à la volée

À la votre!

Questions connexes