首页 > 代码库 > C# 通过 AppDomain 应用程序域实现程序集动态卸载或加载

C# 通过 AppDomain 应用程序域实现程序集动态卸载或加载

  AppDomain 表示应用程序域,它是一个应用程序在其中执行的独立环境。每个应用程序只有一个主应用程序域,

但是一个应用程序可以创建多个子应用程序域。

  因此可以通过 AppDomain 创建新的应用程序域,在新创建的子应用程序域中加载执行程序集并且在执行完毕后释放程序集资源,来实现系统在运行中程序集的动态加载或卸载,从而达到系统运行中程序集热更新的目的。

主应用程序入口:

using Kernel.ServiceAgent;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Reflection;using System.Runtime.Remoting;using System.Text;using System.Threading;using System.Threading.Tasks;namespace Kernel.App{    class Program    {        static void Main(string[] args)        {            Console.WriteLine("");            using (ServiceManager<IObjcet> manager = new ServiceManager<IObjcet>())             {                string result = manager.Proxy.Put("apprun one");                Console.WriteLine(result);                Console.WriteLine("");                Console.WriteLine("                         Thread AppDomain info                             ");                Console.WriteLine(manager.CotrProxy.FriendlyName);                Console.WriteLine(Thread.GetDomain().FriendlyName);                Console.WriteLine(manager.CotrProxy.BaseDirectory);                Console.WriteLine(manager.CotrProxy.ShadowCopyFiles);                Console.WriteLine("");            }            Console.ReadLine();        }    }}

创建新的应用程序域并且在新的应用程序域中调用透明代理类:

using Kernel.Interface;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Reflection;using System.Security.Policy;using System.Text;using System.Threading;using System.Threading.Tasks;namespace Kernel.ServiceAgent{    public class ServiceManager<T> : IDisposable where T : class    {        private AppDomain ctorProxy = null;        /// <summary>        /// 应用程序运行域容器        /// </summary>        public AppDomain CotrProxy        {            get { return ctorProxy; }        }        private T proxy = default(T);        public T Proxy        {            get            {                if (proxy == null)                {                    proxy = (T)InitProxy(AssemblyPlugs);                }                return proxy;            }        }        private string assemblyPlugs;        /// <summary>        /// 外挂插件程序集目录路径        /// </summary>        public string AssemblyPlugs        {            get {                assemblyPlugs = ConfigHelper.GetVaule("PrivatePath");                if (assemblyPlugs.Equals("")){                    assemblyPlugs = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");                }                if (!Directory.Exists(assemblyPlugs))                 {                    Directory.CreateDirectory(assemblyPlugs);                }                return assemblyPlugs;            }            set { assemblyPlugs = value; }        }        public ServiceManager()        {            if (proxy == null)            {                proxy = (T)InitProxy(AssemblyPlugs);            }        }        private T InitProxy(string assemblyPlugs)        {            try            {                //AppDomain.CurrentDomain.SetShadowCopyFiles();                //Get and display the friendly name of the default AppDomain.                //string callingDomainName = Thread.GetDomain().FriendlyName;                //Get and display the full name of the EXE assembly.                //string exeAssembly = Assembly.GetEntryAssembly().FullName;                //Console.WriteLine(exeAssembly);                AppDomainSetup ads = new AppDomainSetup();                ads.ApplicationName = "Shadow";                //应用程序根目录                ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;                //子目录(相对形式)在AppDomainSetup中加入外部程序集的所在目录,多个目录用分号间隔                ads.PrivateBinPath = assemblyPlugs;                ads.CachePath = ads.ApplicationBase;                ads.ShadowCopyFiles = "true";                ads.ShadowCopyDirectories = ads.ApplicationBase;                ads.DisallowBindingRedirects = false;                ads.DisallowCodeDownload = true;                                ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;                //Create evidence for the new application domain from evidence of                Evidence adevidence = AppDomain.CurrentDomain.Evidence;                                // Create the second AppDomain.                ctorProxy = AppDomain.CreateDomain("AD #2", adevidence, ads);                                //Type.GetType("Kernel.TypeLibrary.MarshalByRefType").Assembly.FullName                string assemblyName = Assembly.GetExecutingAssembly().GetName().FullName;                //string assemblyName = typeof(MarshalByRefType).Assembly.FullName                // Create an instance of MarshalByRefObject in the second AppDomain.                 // A proxy to the object is returned.                Console.WriteLine("CtorProxy:" + Thread.GetDomain().FriendlyName);                //TransparentFactory factory = (IObjcet)ctorProxy.CreateInstance("Kernel.TypeLibrary",
               "Kernel.TypeLibrary.TransparentFactory").Unwrap();
TransparentAgent factory = (TransparentAgent)ctorProxy.CreateInstanceAndUnwrap(assemblyName,
                              typeof(TransparentAgent).FullName); Type meetType = typeof(T); string typeName = AssemblyHelper.CategoryInfo(meetType); object[] args = new object[0]; string assemblyPath = ctorProxy.SetupInformation.PrivateBinPath; //IObjcet ilive = factory.Create(@"E:\Plug\Kernel.SimpleLibrary.dll", "Kernel.SimpleLibrary.PlugPut", args); T obj = factory.Create<T>(assemblyPath, typeName, args); return obj; } catch (System.Exception) { throw; } } /// <summary> /// 卸载应用程序域 /// </summary> public void Unload() { try { if (ctorProxy != null) { AppDomain.Unload(ctorProxy); ctorProxy = null; } } catch(Exception) { throw; } } public void Dispose() { this.Unload(); } }}

创建应用程序代理类:

using Kernel.Interface;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace Kernel.ServiceAgent{    public class TransparentAgent : MarshalByRefObject    {        private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;        public TransparentAgent() { }        /// <summary> Factory method to create an instance of the type whose name is specified,        /// using the named assembly file and the constructor that best matches the specified parameters. </summary>        /// <param name="assemblyFile"> The name of a file that contains an assembly where the type named typeName is sought. </param>        /// <param name="typeName"> The name of the preferred type. </param>        /// <param name="constructArgs"> An array of arguments that match in number, order,
     /// and type the parameters of the constructor to invoke, or null for default constructor.
</param> /// <returns> The return value is the created object represented as IObjcet. </returns> public IObjcet Create(string assemblyFile, string typeName, object[] args) { return (IObjcet)Activator.CreateInstanceFrom(assemblyFile, typeName, false, bfi, null, args, null, null).Unwrap(); } public T Create<T>(string assemblyPath, string typeName, object[] args) { string assemblyFile = AssemblyHelper.LoadAssemblyFile(assemblyPath, typeName); return (T)Activator.CreateInstanceFrom(assemblyFile, typeName, false, bfi, null, args, null, null).Unwrap(); } }}

所有涉及到需要动态加载或释放的资源,都需要放在代理类中进行操作,只有在此代理类中进行托管的代码才是属于新建的应用程序域的操作。

using Kernel.Interface;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Reflection;using System.Text;using System.Threading;using System.Threading.Tasks;namespace Kernel.ServiceAgent{    public class AssemblyHelper    {        /// <summary>        /// 获取泛型类中指定属性值        /// </summary>        /// <typeparam name="T"></typeparam>        /// <returns></returns>        public static string CategoryInfo(Type meetType)        {            object[] attrList = meetType.GetCustomAttributes(typeof(CategoryInfoAttribute), false);            if (attrList != null)            {                CategoryInfoAttribute categoryInfo = (CategoryInfoAttribute)attrList[0];                return categoryInfo.Category;            }            return "";        }        public static string LoadAssemblyFile(string assemblyPlugs, string typeName)        {            string path = string.Empty;            DirectoryInfo d = new DirectoryInfo(assemblyPlugs);            foreach (FileInfo file in d.GetFiles("*.dll"))            {                Assembly assembly = Assembly.LoadFile(file.FullName);                Type type = assembly.GetType(typeName, false);                if (type != null)                {                    path = file.FullName;                }            }            return path;        }    }}

读取配置文件信息:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Configuration;namespace Kernel.ServiceAgent{    public class ConfigHelper    {        public static string GetVaule(string configName)        {            string configVaule = ConfigurationManager.AppSettings[configName];            if (configVaule != null && configVaule != "")            {                return configVaule.ToString();            }            return "";        }    }}

配置文件相关配置信息:

<?xml version="1.0" encoding="utf-8" ?><configuration>    <startup>         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />    </startup>    <appSettings>      <add key="PrivatePath" value=http://www.mamicode.com/"E:\Plugins"/>    </appSettings></configuration>

创建接口信息:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Kernel.Interface{    [CategoryInfo("Kernel.SimpleLibrary.PlugPut", "")]    public interface IObjcet    {        void Put();        string Put(string plus);    }}

创建接口自定义属性:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Kernel.Interface{    /// <summary>    /// 设置接口实现类自定义标注属性    /// </summary>    ///    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]    public class CategoryInfoAttribute : Attribute    {        public string Category { get; set; }        public string Describtion { get; set; }        /// <summary>        /// 设置实现类自定义标注属性        /// </summary>        /// <param name="category"></param>        /// <param name="describtion"></param>        public CategoryInfoAttribute(string category, string describtion)        {            this.Category = category;            this.Describtion = describtion;        }    }}

创建继承至IObjcet接口带有具体操作的实现类:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Kernel.Interface;namespace Kernel.SimpleLibrary{    [Serializable]    public class PlugPut : MarshalByRefObject, IObjcet    {        private string plugName = "my plugName value is default!";        public string PlugName        {            get { return plugName; }            set { plugName = value; }        }        public PlugPut() { }                     public PlugPut(string plusName)         {            this.PlugName = plusName;        }        public void Put()        {            Console.WriteLine("Default plug value is:" + plugName);        }        public string Put(string plus)        {            Console.WriteLine("Put plus value is:" + plus);            return ("-------------------- PlugPut result info is welcome -------------------------");        }    }}

小节:

  如果在另一个AppDomain 中加载程序集,然后获取Type,最后在主AppDomain中使用CreateInstance中的Type重载创建对象,结果会是Type所属的程序集会被加入到当前AppDomain中,然后Type的实例会在当前AppDomain中创建。

  只有继承至 MarshalByRefObject 的透明代理类才能够进行跨域操作。

  所以需要在继承至 MarshalByRefObject 的透明代理类中进行相关操作然后返回给主应用程序域,只有在代理类中进行的代码操作才是属于新建的应用程序域。否则任何运行代理类以外的代码都是属于主应用程序域。

C# 通过 AppDomain 应用程序域实现程序集动态卸载或加载