7

I've comp up against a formidable conundrum. Here's my situation:

I'm building an application with a plugin framework. There's a base plugin class that all plugins must extend. Within the same same assembly I have a helper class that will serialize and deserialize classes. It's a generic class and it's used all over the place. The structure is like this:

MyApp.dll
|_ App.cs
|_ HelperCollection.cs
|_ PluginBase.cs

MyPlugin.dll
|_MyPlugin.cs (this extends PluginBase)
|_Foo.cs

The Problem

My problem is the assembly loading and locking files. A requirement of the app is that Plugins can be overwritten at any time. If so, they need to be reloaded. It seems the best way to load an assembly so that it's not locked (that is, I can overwrite it or blow away while the app is still running) is this:

byte[] readAllBytes = File.ReadAllBytes("MyPlugin.dll");
Assembly assembly = Assembly.Load(readAllBytes);

Loading the plugin assembly works just fine, no problems there. I get an exception when, from within MyPlugin.cs, which is in the plugin assembly, I tried to use the HelperCollection to do deserialization. An example could be something like this:

// HelperCollection uses XmlSerializer under the covers
List<Foo> settingCollection = HelperCollection<Foo>.Deserialize("mysettings.xml");

It's blowing up and throwing an InvalidCastException saying that it's "Unable to cast object of type 'List[Foo]' to 'List[Foo]'". After much research I finally found why. It's getting loaded in the LoadNeither binding context.

When Foo is loaded (from MyPlugin.dll) it's in the LoadNeither binding context while the assembly containing the type for the type conversion (in my case, MyApp.dll) is loaded in Default context. So even though they have the same name they are not considered the same type. It's doing this because I'm using Assembly.Load(byte[]).

Questions

How can I get around this? How can I,

  1. Load an assembly and not lock the file, and
  2. Provide the right binding context so I can cast objects which are located in the loaded assembly.

Sorry for the wall of text, just wanted to get all the relevant info out there.

Community
  • 1
  • 1
JonH
  • 821
  • 11
  • 19

2 Answers2

6

Have you tried shadow copying?

When you're using shadow copying, .NET copies the assembly to a temp directory and loads it from there (so it's the temp file that's locked, not the original assembly) -- but all the binding rules are based on the original file location.

Joe White
  • 94,807
  • 60
  • 220
  • 330
  • Good call, ultimately I implemented this because it seemed more elegant - and it did work - so point goes to you. However, as I started to implement loading plugins in a separate appdomain I ran into problems of communicating across the domain. Everything I passed across the domain had to inherit from MarshalByRefObject to work and it was turning into an ugly mess. I'm looking into MEF though, it looks like it'll be a simpler solution than building my own home-grown stuff. – JonH Mar 16 '11 at 16:37
2

Just an idea from the top of my head:

What about having a Plugin directory where the users may drop their Plugins into. Then in your application, you monitor that folder and copy the assemblies to the bin or whatever. Then you load them the "normal" way, which may lock them.

When the user drops new plugins, you kill the plugin app domain and reload them.

Just an idea.

Mikael Östberg
  • 16,982
  • 6
  • 61
  • 79
  • +1. This allows you to watch the 'drop' directory for file system updates and recycle and refresh your plugin AppDomain(s) easily. – captain numerica Mar 11 '11 at 00:34
  • That's an intriguing idea! I was thinking of copying them to a temp directory in Path.GetTempPath(). If I did that, is there a way to load them using something other than Assembly.LoadFrom()? Using that method gives me the LoadFrom binding context which seems to have a lot of disadvantages. – JonH Mar 12 '11 at 07:30
  • 1
    Yes, you can use `Assembly.Load()`. Check this out: http://stackoverflow.com/questions/2886397/load-and-execute-code-from-an-assembly-without-locking-the-file Btw, Have you had a look at MEF? – Mikael Östberg Mar 12 '11 at 10:41
  • I hadn't before...but the more I look into it, the more it looks like that's the way to go. I'm reinventing the wheel it seems, they've already created a framework for working with plugins. – JonH Mar 16 '11 at 16:31
  • You are indeed reinventing the wheel, but doing this the old way will mean that: 1, you are not dependent on MEF and 2, You will learn a great deal along the way. And it's always fun to roll your own thing. – Mikael Östberg Mar 16 '11 at 18:53