Exception with GetExportedValues

Dec 10, 2011 at 11:18 PM
Edited Dec 10, 2011 at 11:19 PM

Hi,

I have my plugins loading fine with MEF and use a filtered catalog to determine those to load. Using your FiilteredCatalog class example from the documentation area.

However I've found that some DLL's cause the following error when the code gets to my Container.GetExportedValues (red highlight below) code statement.

           Dim catalog As New AggregateCatalog
           ' Look for Plugins in the ./Plugins directory.
           catalog.Catalogs.Add(New DirectoryCatalog("Plugins"))
           ' Look for Plugins in subdirectories under ./Plugins.
           For Each currentDirPath As String In Directory.GetDirectories("Plugins")
               catalog.Catalogs.Add(New DirectoryCatalog(currentDirPath))
           Next
           ' Load all found plugin Interface Types using MEF
           Dim filteredCat = New FilteredCatalog(catalog, Function(def) def.Metadata.ContainsKey("scope") AndAlso def.Metadata("scope").ToString() = "plugintype1”)
           Dim Container = New CompositionContainer(filteredCat)
           PluginRepository.ClientPluginCore = Container.GetExportedValues(Of IClientPluginCore)()

So why does this rouge DLL (that just happens to be in the folder) basically cause this exception and stop ALL other good plugins from loading?

Surely the filtered catalog should filter it out as it doesnt contain the metadata tags nor does it comply with any of my exported interfaces.

Remove the rogue DLL and all is fine.  Cant leave it like that though as will be hard to debug in future if another DLL creeps in to cause the same effect.

I'm confused....helppppppp!

 

Julian

System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
   at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
   at System.Reflection.Assembly.GetTypes()
   at System.ComponentModel.Composition.Hosting.AssemblyCatalog.get_InnerCatalog()
   at System.ComponentModel.Composition.Hosting.AssemblyCatalog.get_Parts()
   at lambda_method(Closure , ComposablePartCatalog )
   at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
   at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
   at System.ComponentModel.Composition.Primitives.ComposablePartCatalog.GetExports(ImportDefinition definition)
   at System.ComponentModel.Composition.Hosting.CatalogExportProvider.GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.AggregateExportProvider.GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.TryGetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition, IEnumerable`1& exports)
   at System.ComponentModel.Composition.Hosting.CompositionContainer.GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExportedValuesCore[T](String contractName)
  ....

 

The DLL that I've found that is causing this is an old plugin.dll i wrote before the move to MEF.  It uses an Interface contract but not the one in the code above and in fact the Interface isnt used in the project at all.

Dec 10, 2011 at 11:47 PM
Edited Dec 10, 2011 at 11:48 PM

OK I made a bit more progress on this;  I enabled loader exceptions and got this:

System.IO.FileNotFoundException: Could not load file or assembly 'PluginInterfaces, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. File name: 'PluginInterfaces, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null'

So this is the old plugin Interface file (non MEF solution that I used to use) the "rogue" plugin used to use which isnt there anymore in the folder;  just the "rogue" dll itself.

So why doesnt MEF just ignore this orphaned DLL instead of crashing with and exception.  Any ideas on how to trap or mitigate somehow so the "good" plugins do load OK?

Julian

 

Dec 11, 2011 at 2:31 PM

I've found a workaround via playing in the debugger, which skips the folder that contains the bad plugin (could be any DLL in that folder that causes it to fail) as follows:

         Dim PartType As Type
            ' Look for Plugins in subdirectories under ./Plugins.
            For Each currentDirPath As String In Directory.GetDirectories("Plugins")
                Try
                    ' Test for valid DLL Parts
                    For Each Part In New DirectoryCatalog(currentDirPath).Parts
                        PartType = Part.GetType ' This will fail for any badly formed DLL's
                    Next
                    ' If code gets this far then parts appear valid
                    catalog.Catalogs.Add(New DirectoryCatalog(currentDirPath))
                Catch ex As Exception
                    ' Output Error Message to file and Monitor Window
                    WriteLogFile("Problem loading Plugin from " & currentDirPath.ToString & vbCrLf & "Details follow:" & vbCrLf)
                    WriteOutput("+ Problem loading Plugin from " & currentDirPath.ToString)
                    ' Invalid Plugin (something is wrong)
                    If TypeOf ex Is System.Reflection.ReflectionTypeLoadException Then
                        Dim typeLoadException = TryCast(ex, ReflectionTypeLoadException)
                        Dim loaderExceptions = typeLoadException.LoaderExceptions
                        For Each e In loaderExceptions
                            WriteLogFile(e.ToString & vbCrLf)
                        Next
                    End If
                End Try
            Next

Seems it's the DirectoryCatalog that fails in that one of the types has an invalis part.  So I loop around the parts doing a part.gettype  and when it gets to the bad one it is caught (and I can trap error) and then at least the other plugins load ok.

So probably really novice code but seems to work.

Any better ideas greatfully received.

Julian

 

 

Dec 13, 2011 at 5:49 PM

Hi Julian,

Glad you were able to work through this. MEF doesn’t do any filtering of invalid DLLs because failing fast ensures development and deployment problems are identified early – ignoring broken/stale/invalid DLLs would make it more likely that these kinds of issues would slip through to production unnoticed, and thus cause more damage in the long run.

Your solution below and other variants on it are the recommended approach.

Regards,
Nick

From: juwi_uk [email removed]
Sent: Sunday, December 11, 2011 6:31 AM
To: Nicholas Blumhardt
Subject: Re: Exception with GetExportedValues [MEF:282519]

From: juwi_uk

I've found a workaround via playing in the debugger, which skips the folder that contains the bad plugin (could be any DLL in that folder that causes it to fail) as follows:

Dim PartType As Type
' Look for Plugins in subdirectories under ./Plugins.
For Each currentDirPath As String In Directory.GetDirectories("Plugins")
Try
' Test for valid DLL Parts
For Each Part In New DirectoryCatalog(currentDirPath).Parts
PartType = Part.GetType ' This will fail for any badly formed DLL's
Next
' If code gets this far then parts appear valid
catalog.Catalogs.Add(New DirectoryCatalog(currentDirPath))
Catch ex As Exception
' Output Error Message to file and Monitor Window
WriteLogFile("Problem loading Plugin from " & currentDirPath.ToString & vbCrLf & "Details follow:" & vbCrLf)
WriteOutput("+ Problem loading Plugin from " & currentDirPath.ToString)
' Invalid Plugin (something is wrong)
If TypeOf ex Is System.Reflection.ReflectionTypeLoadException Then
Dim typeLoadException = TryCast(ex, ReflectionTypeLoadException)
Dim loaderExceptions = typeLoadException.LoaderExceptions
For Each e In loaderExceptions
WriteLogFile(e.ToString & vbCrLf)
Next
End If
End Try
Next

Seems it's the DirectoryCatalog that fails in that one of the types has an invalis part. So I loop around the parts doing a part.gettype and when it gets to the bad one it is caught (and I can trap error) and then at least the other plugins load ok.

So probably really novice code but seems to work.

Any better ideas greatfully received.

Julian