[转]Using AppDomain to Load and Unload Dynamic Assemblies----work correctly

Recently a project I was working on required us to dynamically load an assembly at runtime, and invoke a method on the newly loaded assembly. After we had this working, I noticed that when we tried to then load a newer version of the dynamic assembly, the newer bits weren't being picked up, and old code was executing. Time to investigate a bit further:

I came across a great article by Jon Shemitz that pointed me in the right direction. AppDomains are a powerful concept in .NET, however as most applications you write live within a single AppDomain, you may not have noticed them before. My problem was that in .NET, you cannot simply unload an assembly directly. However, if we were to create a new AppDomain, load our dynamic assembly within it, call the methods needed, and then unload the AppDomain, we could load, unload, update versions and reload until our hearts content. The final piece of our puzzle (that we already had in place) is to use an interface to allow us the ability to invoke methods on the loaded assembly from our manager executable.

The solution I've created to demonstrate this concept uses four projects:

AppDomainLoad: A basic console application that simply news up an instance of the Manager class and calls the entry method.

WorkerShared: This project holds any objects that will be used by both the Manager and Worker classes. This is the only assembly that will be loaded by both AppDomains. Our shared interface, IWorker, lives in this project. A utility class calls AppDomain.CurrentDomain.GetAssemblies() to let us know what really is being loaded at runtime. This is a handy method to use, even in standard single AppDomain applications, to ensure you only load what you need.

WorkerManager: The .dll that creates a separate AppDomain, loads an instance of the dynamic assembly, and casts this instance to the IWorker interface. After all this, the Manager simply calls a method available from the interface. Once the work is complete, the AppDomain is unloaded, which drops any assemblies loaded within the worker domain.

DisconnectedWorker: Where the rubber hits the road, this project executes the actual work. The worker class a) implements the IWorker interface b) derives from MarshalByRefObject, which allows access to objects across domain boundaries (because these interactions are basically remoting under the covers), and c) is marked as Serializable.

One final note, after building the projects, I placed the WorkerShared.dll and DisconnectedWorker.dll bits into the C:\BlogProjects\AssemblyPool directory. This is only to simplify the project code; your project can locate these assemblies however you need. For this example, create the directory and drop the assemblies.

Here's the example code. The key classes' code is shown below. Let me know if you have any questions.

IWorker interface

namespace  WorkerShared
{
    
public interface  IWorker
    {
        
// Define required methods:
         void  DoWork() ;
    
}
}

WorkerManager

using  System ;
using 
WorkerShared ;

namespace 
WorkerManager
{
    
public class  Manager
    {
        
private const string  CONFIG_ASSEMBLY_POOL  @"C:\BlogProjects\AssemblyPool" ;
        private const string 
CONFIG_DYNAMIC_ASSEMBLY_PROJECT  "DisconnectedWorker" ;
        private const string 
CONFIG_DYNAMIC_ASSEMBLY_FULLY_QUALIFIED_NAME  "DisconnectedWorker.Worker" ;
        
        private const string 
FORMAT_WORKER_DOMAIN_FRIENDLY_NAME  "Dynamic Worker Domain" ;
        private const string 
FORMAT_WORKER_DOMAIN_CREATED  "Created '{0}' AppDomain" ;
        private const string 
FORMAT_WORKER_DOMAIN_UNLOADED  "Unloaded '{0}' AppDomain" ;
        private const string 
FORMAT_WORK_COMPLETE  "All work complete." ;
        private const string 
FORMAT_START_ASSEMBLIES  "Starting Assemblies Loaded:" ;
        private const string 
FORMAT_END_ASSEMBLIES  "Post-unload Assemblies Loaded:" ;

        public void 
RunAppDomainExample()
        {
            
// Show current assemblies before we start:
             Console.WriteLine(FORMAT_START_ASSEMBLIES) ;
            
Utilities.WriteCurrentLoadedAssemblies() ;

            
// create display name for appDomain
             string  workerName  = string .Format(FORMAT_WORKER_DOMAIN_FRIENDLY_NAME) ;

            
// Construct and setup appDomain settings:
             AppDomainSetup ads  = new  AppDomainSetup() ;

            
ads.ApplicationBase  CONFIG_ASSEMBLY_POOL ;
            
ads.DisallowBindingRedirects  = false;
            
ads.DisallowCodeDownload  = true;
            
ads.ConfigurationFile  AppDomain.CurrentDomain.SetupInformation.ConfigurationFile ;

            
// Create domain
             Console.WriteLine() ;
            
AppDomain workerAppDomain  AppDomain.CreateDomain(workerName,  null , ads) ;
            
Console.WriteLine(FORMAT_WORKER_DOMAIN_CREATED, workerName) ;

            
// do work on proxy
             IWorker workerInstance  =
                
(IWorker)
                workerAppDomain.CreateInstanceAndUnwrap(CONFIG_DYNAMIC_ASSEMBLY_PROJECT,
                            &nbs p;                           CONFIG_D YNAMIC_ASSEMBLY_FULLY_QUALIFIED_NAME)
;

            
// Execute the task by invoking method on the interface instance
             workerInstance.DoWork() ;

            
// Unload worker appDomain
             AppDomain.Unload(workerAppDomain) ;
            
Console.WriteLine(FORMAT_WORKER_DOMAIN_UNLOADED, workerName) ;
            
Console.WriteLine() ;

            
// Show current assemblies before we start:
             Console.WriteLine(FORMAT_END_ASSEMBLIES) ;
            
Utilities.WriteCurrentLoadedAssemblies() ;

            
Console.WriteLine(FORMAT_WORK_COMPLETE) ;
            
Console.ReadLine() ;
        
}
    }
}

DisconnectedWorker

using  System ;
using 
WorkerShared ;

namespace 
DisconnectedWorker
{
    [Serializable]
    
public class  Worker : MarshalByRefObject, IWorker
    {
        
public void  DoWork()
        {
            
// Show the assemblies loaded in this appDomain
             Utilities.WriteCurrentLoadedAssemblies() ;
        
}
    }
}

Utilities

using  System ;
using 
System.Reflection ;

namespace 
WorkerShared
{
    
public static class  Utilities
    {
        
public static void  WriteCurrentLoadedAssemblies()
        {
            Assembly[] assemblies 
AppDomain.CurrentDomain.GetAssemblies() ;

            foreach 
(Assembly assembly  in  assemblies)
            {
                Console.WriteLine(
"Loaded:  {0}." , assembly.ManifestModule.Name) ;
            
}
        }
    }
}

Payday: The Project Output

Starting Assemblies Loaded:
Loaded: mscorlib.dll.
Loaded: Microsoft.VisualStudio.HostingProcess.Utilities.dll.
Loaded: System.Windows.Forms.dll.
Loaded: System.dll.
Loaded: System.Drawing.dll.
Loaded: Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll.
Loaded: AppDomainLoad.vshost.exe.
Loaded: System.Data.dll.
Loaded: System.Xml.dll.
Loaded: AppDomainLoad.exe.
Loaded: WorkerManager.dll.
Loaded: WorkerShared.dll.

Created 'Dynamic Worker Domain' AppDomain
Loaded: mscorlib.dll.
Loaded: Microsoft.VisualStudio.HostingProcess.Utilities.dll.
Loaded: DisconnectedWorker.dll.
Loaded: WorkerShared.dll.
Unloaded 'Dynamic Worker Domain' AppDomain

Post-unload Assemblies Loaded:
Loaded: mscorlib.dll.
Loaded: Microsoft.VisualStudio.HostingProcess.Utilities.dll.
Loaded: System.Windows.Forms.dll.
Loaded: System.dll.
Loaded: System.Drawing.dll.
Loaded: Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll.
Loaded: AppDomainLoad.vshost.exe.
Loaded: System.Data.dll.
Loaded: System.Xml.dll.
Loaded: AppDomainLoad.exe.
Loaded: WorkerManager.dll.
Loaded: WorkerShared.dll.
All work complete.

转载于:https://www.cnblogs.com/joe-yang/archive/2009/10/03/1577781.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值