Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

Long: Do I really need an AppDomain?

I need to edit a tree structure, where each tree consists of nodes with a timestamp, a filename, and list of children - and extra contents, which varies from tree to tree. (That is, each tree consists of only one type of node, but This tree's nodes may have only a ShortName string while That tree's nodes may have OnEnter and OnExit strings.) I could implement this with a Hashtable, but I'd really prefer to use true, Reflection-compatible class definitions, as that way the Node fields are accessible to my scripting engine without either compiler magic or explicit function calls and lots of casting.

The first version of the editor used a compiled-in Node class. I wrote a function that finds Node fields or properties with a [Node] custom attribute and returns an array of MemberInfo/Attribute pairs. The
current version of the editor uses this list to create the node editing widgets, and move data between the UI and each Node.

I also wrote a method that can take a list of these MemberInfo/Attribute pairs and create a dll containing only a class with the specified fields. The next step is to write a Node field UI that lets the user edit a MemberInfo/Attribute pair list, so that the
name of the assembly that contains the Node class can be persisted with the tree header.

En route, I ran into the problem that once I load the Node assembly, I can't rewrite the dll until I shut the app down - I can not rewrite the dll after editing the node structure. Loading the assembly SEEMS
to keep the dll file open. Poking around Google and the dox, I see that I can't unload assemblies; that I have to use an AppDomain.

OK, that works, once I move the Node's ancestral class into its own dll. I can load the Node dll; unload it by unloading the AppDomain; reload it. Great.

My question (finally!) is: Do I really need an AppDomain? It seems like such overkill. I don't mind the memory waste from having multiple versions of the tiny Node assembly loaded; I'm pretty sure I can handle any type identity issues just by only doing a GetType("Node") once, when I actually load the assembly, and then passing around the
resulting Type value.

Is there some way to break the connection between a loaded assembly and the file it was loaded from, so the file can be rewritten?

Jon Shemitz
Tuesday, February 18, 2003

Instead of loading the assembly directly, why not copy the assembly to a temporary location (perhaps using System.IO.Path.GetTempPath() and GetTempFileName() and File.Copy()) and load the assembly from there?

This is similar to what ASP.NET does so that you can overwrite DLLs whilst a site is running.

Duncan Smart
Tuesday, February 18, 2003

That's a good suggestion, but probably better suited to ASP.Net, which is already using AppDomains to load and unload code. I'd need to build some sort of mechanism to delete the temp dlls after the editor closes - probably launching a cleanup app.

I posted to a different forum, and was pointed to the Assembly.Load(byte[]) overload, which works perfectly.

Jon Shemitz
Tuesday, February 18, 2003

Um, I am pretty sure that the CLR loads assemblies ALWAYS into an App Domain.  The App Domain is similar to a process, but not quite.  Instead of loading the assembly form it's own directory, copy the assembly to some temp location (which was suggested above), or put the dll into the global assembly cache (which stores the assembly independently of the physical dll).  The problem with the gac is that you would need to delete the old version of the dll and add in the new version each time.

Tim
Thursday, February 27, 2003

Yes, but there's a huge difference between loading into the current (default) AppDomain and creating one specifically so you can unload code. Aside from the overhead of creating and unloading the AppDomain, AppDomains imply strong isolation: I can't simply load a class type in This AppDomain and then create an instance in That AppDomain ... there's all sorts of marshalling mishegos.

The Load(byte[]) approach worked fine in my tree editor ... but blew up when I tried to XmlSerialize data structures containing the new class. Xml serialization complained about dynamic types; wanted an assembly on disk. But the Load From Temp File approach doesn't support serialization, either. (I got a bit detoured, and just got back to this.) Maybe I'll try your GAC suggestion, but I sure don't like the sound of it.

Jon Shemitz
Monday, March 03, 2003

Take a look at the AppDomain.DoCallback, using SetData & GetData methods to exchange data between app domains.  It sounds to me like you are dynamically generating code at runtime and you would like to generate the same dll that you loaded the type from.  Have you tried to generate a temporary dll, write the types to this dll, then rename the temp dll to the original dll?  Or could you somehow load the type information from a file at startup and then create the types from the file using reflection (System.Reflection.Emit)? 

Tim
Monday, March 03, 2003

Also take a look at 'shadow copying' with AppDomains.  Look at the AppDomainSetup.ShadowCopyDirectories which indicates the parent directories of the assemblies that you want to be shadow copied.  The AppDomainSetup.CachePath indicates the root of the real directory where the assembly is loaded from.  The CLR will then read the DLL from the ShadowCopyDirectories starting location and copy the DLL from there to the CachePath destination location and then load the DLL from the destination location.  Then you can update the DLL in the starting location with no contention.

Tim
Monday, March 03, 2003

*  Recent Topics

*  Fog Creek Home