NET对象的跨应用程序域访问

 
 在当时此刻的种类开拓中,布满式开荒已经日趋改为主流。一个连串只要未有使用分布式布局,都倒霉意思跟外人说那是三个完完全全的项目。那句话纵然有个别偏激,可是随着大家对功效的必要在滋长,以致产物要求进级客户体验。独有在软件项目的频率和心得做到高素质,才方可拿走客商和商海。

   对于.NET项目,大家接受比较多的遍及式构造有Webservice,.Net
remoting,MSMQ,WCF,WebAPI等等,大家在应用这么些框架的时候,从那些布满式框架中拿走了很好的客商体验。在.NET项目中,布满式布局对项目标付出也可能有一点都不小的成效升高。

 
 非常多少人会问,这个分布式框架的平底原理是何等吗?大概何人也不敢轻言几句就能够描述完结,在这一个博文体系中,就是简单来说述一下那个布满式布局的平底实现原理。

 
 本文首要讲师对象在行使程序域中的传递。首要教学使用程序域的大器晚成对基本指标,对于利用程序域的操作现身的少之甚少,所以在那边给出的是前后相继集的有些基本操作。如有不足之处,还望多多指正。

一.AppDomain解析:

   
 AppDomain在大多地方都以被翻译为“应用程序域”,在本文中也将选用这大器晚成翻译。对于.NET的开辟者,对于CLCRUISER应该是最熟习然则了,CLTiggo相近于java的JVM。在CL帕杰罗中,AppDomain规定了代码的实行范围,提供了错误隔绝的水平,提供了一个平安隔开度,并且具备本人的财富。AppDomain的有声有色作用,犹如下图:

  大红鹰葡京会 1

   1.AppDomain概述:

大红鹰葡京会,     
AppDomain相近与系统的长河,进程是有操作系统进行创办,AppDomain是由CLOdyssey实行创办。叁个加以的AppDomain必需驻留在二个操作系统的经过中,而多个加以的经过能够借宿多少个AppDomain。好似下图:

   大红鹰葡京会 2

     
如上海教室所示,二个对象恰巧存放在四个AppDomain种,值也生龙活虎律。三个AppDomain中的对象援用必需是援用同风度翩翩AppDomain中的对象,AppDomain的行为就就好像有所本人个人的地址空间。假若五个AppDomain须求采取三个种类,必需为每一个AppDomain分别开始化和分配一回品种。必得为顺序用到花色的AppDomain分别加载和开端化二遍品种的法子和次序集。进度种的逐豆蔻梢头AppDomain要保证项目标两样拷贝。对于项目标静态子类,每一个AppDomain都有其和好的民用别本。

     AppDomain的能源犹如图:

大红鹰葡京会 3

     
对于应用AppDomain的财富被加载,一向在内部存款和储蓄器中,卸载AppDomain能源是并世无两卸载模块可能程序集的门径,卸载AppDomain能源也是回收类型静态字段所占内部存款和储蓄器的独占鳌头方法。

   
 在上边提到过操作系统的线程与AppDomain相像,在CLEnclave中定义了System.Threading.Thread,在AppDomain中表示为可调治的实体,在那地提出三个新的概念,那正是“软线程”和“硬线程”,看名就会知道意思,操作系统的线程被喻为“硬线程”,CL逍客中的System.Threading.Thread被喻为“软线程”。多少个CL汉兰达软线程对象驻留在贰个规定的AppDomain中;贰个加以的AppDomain恐怕有多少个软线程对象。在脚下的CLEvoque中,对于给定的AppDomain,硬线程至多有三个软线程对象属于他,如果三个硬线程运维在多少个AppDomain中,每一种AppDomain都会有三个一言以蔽之的软线程对象归属该线程。当给定的硬线程步向AppDomain后,就能够拿到相似的软线程对象。

   2.AppDomain焦点对象深入分析:

   
 上面介绍了有的AppDomain的基本概念,接下去大家来轻松询问一下AppDomain的连带操作和宗旨目的。在.NET种能够经过System.AppDomain类型访谈AppDomain。在那我们切实驾驭一下System.AppDomain类型的艺术和天性。对于此类的注明:https://msdn.microsoft.com/en-us/library/system.appdomain(v=vs.110).aspx。  

   (1卡塔尔(قطر‎.CurrentDomain:获取当前Thread 的当前应用程序域。

public static AppDomain CurrentDomain
    {
      get
      {
        return Thread.GetDomain();
      }
    }

   
由上述代码可以预知,该属性为贰个静态属性,并且独有八个只读属性。该属性只是简短地领取存款和储蓄在硬线程的TLS(线程本地存款和储蓄区)中的AppDomain援用。你能够在Thread.CurrentThread属性中,从硬线程的TLS中领到当前的软线程对象。 

   (2卡塔尔.GetData(卡塔尔(英语:State of Qatar):为内定名称获取存款和储蓄在当下应用程序域中的值。

[SecuritySafeCritical]
    public object GetData(string name)
    {
      if (name == null)
        throw new ArgumentNullException("name");
      switch (AppDomainSetup.Locate(name))
      {
        case -1:
          if (name.Equals(AppDomainSetup.LoaderOptimizationKey))
            return (object) this.FusionStore.LoaderOptimization;
          object syncRoot = ((ICollection) this.LocalStore).SyncRoot;
          bool lockTaken = false;
          object[] objArray;
          try
          {
            Monitor.Enter(syncRoot, ref lockTaken);
            this.LocalStore.TryGetValue(name, out objArray);
          }
          finally
          {
            if (lockTaken)
              Monitor.Exit(syncRoot);
          }
          if (objArray == null)
            return (object) null;
          if (objArray[1] != null)
            ((IPermission) objArray[1]).Demand();
          return objArray[0];
        case 0:
          return (object) this.FusionStore.ApplicationBase;
        case 1:
          return (object) this.FusionStore.ConfigurationFile;
        case 2:
          return (object) this.FusionStore.DynamicBase;
        case 3:
          return (object) this.FusionStore.DeveloperPath;
        case 4:
          return (object) this.FusionStore.ApplicationName;
        case 5:
          return (object) this.FusionStore.PrivateBinPath;
        case 6:
          return (object) this.FusionStore.PrivateBinPathProbe;
        case 7:
          return (object) this.FusionStore.ShadowCopyDirectories;
        case 8:
          return (object) this.FusionStore.ShadowCopyFiles;
        case 9:
          return (object) this.FusionStore.CachePath;
        case 10:
          return (object) this.FusionStore.LicenseFile;
        case 11:
          return (object) (bool) (this.FusionStore.DisallowPublisherPolicy ? 1 : 0);
        case 12:
          return (object) (bool) (this.FusionStore.DisallowCodeDownload ? 1 : 0);
        case 13:
          return (object) (bool) (this.FusionStore.DisallowBindingRedirects ? 1 : 0);
        case 14:
          return (object) (bool) (this.FusionStore.DisallowApplicationBaseProbing ? 1 : 0);
        case 15:
          return (object) this.FusionStore.GetConfigurationBytes();
        default:
          return (object) null;
      }
    }

 
 每三个AppDomain有投机的条件属性集,可以经过SetData和GetData方法访谈,在这里间给出了GetData(卡塔尔方法的源码。该情势采纳三个string参数,预订义应用程序域属性的名号,或已定义的运用程序域属性的称号。重返二个天性的值,或
null(假使属性官样文章)。AppDomainSetup类为叁个密封类,表示能够增加到System.AppDomain的实例的程序集绑定音讯。

 
 (3卡塔尔(قطر‎.CreateDomain:使用内定的称谓、证据和行使程序域设置消息创设新的行使程序域。

[SecuritySafeCritical]
    [SecurityPermission(SecurityAction.Demand, ControlAppDomain = true)]
    public static AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info)
    {
      return AppDomain.InternalCreateDomain(friendlyName, securityInfo, info);
    }

 
 该情势存在多少个重载,选择五个参数,域的友爱名称。friendlyName:此友好名称可在顾客分界面中体现以标记域;securityInfo:鲜明代码标志的证据,该代码在使用程序域中运维。传递
null
以应用当前应用程序域的凭据。info:包括应用程序域初叶化音讯的对象。该情势重临二个新创立的选拔程序域。

 
 (4卡塔尔国.ExecuteAssembly(卡塔尔:使用钦命的凭证和实参实行钦定文件中带有的程序集。

 [Obsolete("Methods which use evidence to sandbox are obsolete and will be removed in a future release of the .NET Framework. Please use an overload of ExecuteAssembly which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")]
    public int ExecuteAssembly(string assemblyFile, Evidence assemblySecurity, string[] args)
    {
      if (assemblySecurity != null && !this.IsLegacyCasPolicyEnabled)
        throw new NotSupportedException(Environment.GetResourceString("NotSupported_RequiresCasPolicyImplicit"));
      RuntimeAssembly assembly = (RuntimeAssembly) Assembly.LoadFrom(assemblyFile, assemblySecurity);
      if (args == null)
        args = new string[0];
      return this.nExecuteAssembly(assembly, args);
    }

 
 当成立贰个AppDomain后,能够使用少年老成多级手艺强逼它加载和举行代码,能够应用ExecuteAssembly方法。该措施将对象AppDomain加载到程序集中,何况施行其主入口点。在父AppDomain种,ExecuteAssembly方法不会加载或然开头化内定的次序集。ExecuteAssembly是二个联袂的例程,那就象征调用者将被打断,直到程序的Main方法把调节权交还运转时。

 
 ExecuteAssembly方法存在多少个重载版本,在那边只拿出叁个版本来表明。该方式选择三个参数,assemblyFile:包涵要进行顺序集的文书的名目;assemblySecurity:为顺序集提供的凭据;args:程序集的入口点的实参。该方式重返 程序集的入口点重返的值。该方法应用Assembly.LoadFrom来加载程序集。有关程序集的源委就要下风度翩翩篇解说。

 
 (5卡塔尔国.DoCallBack(卡塔尔:在另三个施用程序域中履行代码,该行使程序域由钦命的委托标志。

 public void DoCallBack(CrossAppDomainDelegate callBackDelegate)
    {
      if (callBackDelegate == null)
        throw new ArgumentNullException("callBackDelegate");
      callBackDelegate();
    }

 
 那个钦定方法必需是静态的,並且它的签订与CrossAppDomainDelegate具名匹配。

三.程序集操作实例:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace AppDomainToolkit
{

    /// <summary>
    /// 用于确定加载器应加载哪些加载上下文程序集。
    /// </summary>
    public enum LoadMethod
    {
        /// <summary>
        /// 将程序集加载到LoadFrom上下文中,这将使程序集及其所有引用被发现
                 ///并加载到目标应用程序域中。 尽管它对DLL地狱的倾向,这可能是去的方式
                 /// default,只要确保将应用程序的基本目录传递给AssemblyResolver实例等
                 ///可以正确解析引用。 这也允许同时加载同名的多个程序集
                 ///维护单独的文件名。 这是推荐的方式。
        /// </summary>
        LoadFrom,

        /// <summary>
        /// 使用原始文件名将组合件加载到内存中。 这将以匿名方式加载程序集,因此它不会有
                 ///一个加载上下文。 使用这个,如果你想要的位加载,但确保通过这个文件所在的目录
                 /// AssemblyResolver实例,以便您可以再次找到它。 这是类似于LoadFrom,除非你没有得到免费
                 ///通过融合查找已经存在的程序集名称。 使用它可以更好地控制汇编文件加载。
        /// </summary>
        LoadFile,

        /// <summary>
        /// 使用原始文件名将目标程序集的位加载到内存中。 这本质上是一个动态组件
                 ///为所有的CLR关心。 你将永远不能找到这个与程序集解析器,所以不要使用这,除非你看
                 ///按名称。 小心这一个。
        /// </summary>
        LoadBits
    }

    /// <summary>
    /// 这个类将会把程序集加载到它加载到的任何应用程序域中。 这只是一个简单的方便
    /// wrapper环绕静态Assembly.Load *方法,主要的好处是能够加载程序集
    ///匿名按位。 当您以这种方式加载程序集时,不会有任何DLL文件的锁定。
    /// </summary>
    public class AssemblyLoader : MarshalByRefObject, IAssemblyLoader
    {
        #region Public Methods

        /// <inheritdoc /> 
        /// <remarks>
        /// 如果此实例的LoadMethod设置为LoadBits,并且PDB文件的路径未指定,那么我们将尝试猜测
                 ///到PDB的路径并加载它。 注意,如果一个程序集被加载到内存中而没有调试符号,那么
                 /// image将被抛出。 警惕这个。 使用LoadBits方法加载程序集不会锁定
                 /// DLL文件,因为整个程序集被加载到内存中并且文件句柄被关闭。 但是,
                 ///以这种方式加载的程序集不会有与之关联的位置,因此您必须键入程序集
                 ///它的强名。 当将同一程序集的多个版本加载到一个程序集时,这可能会导致问题
                 ///应用程序域。
        /// </remarks>
        public Assembly LoadAssembly(LoadMethod loadMethod, string assemblyPath, string pdbPath = null)
        {
            Assembly assembly = null;
            switch (loadMethod)
            {
                case LoadMethod.LoadFrom:
                    assembly = Assembly.LoadFrom(assemblyPath);
                    break;
                case LoadMethod.LoadFile:
                    assembly = Assembly.LoadFile(assemblyPath);
                    break;
                case LoadMethod.LoadBits:

                    // Attempt to load the PDB bits along with the assembly to avoid image exceptions.
                    pdbPath = string.IsNullOrEmpty(pdbPath) ? Path.ChangeExtension(assemblyPath, "pdb") : pdbPath;

                    // Only load the PDB if it exists--we may be dealing with a release assembly.
                    if (File.Exists(pdbPath))
                    {
                        assembly = Assembly.Load(
                            File.ReadAllBytes(assemblyPath),
                            File.ReadAllBytes(pdbPath));
                    }
                    else
                    {
                        assembly = Assembly.Load(File.ReadAllBytes(assemblyPath));
                    }

                    break;
                default:
                    // In case we upadate the enum but forget to update this logic.
                    throw new NotSupportedException("The target load method isn't supported!");
            }

            return assembly;
        }

        /// <inheritdoc />
        /// <remarks>
        /// 这个实现将执行目标程序集的尽力负载,它是必需的引用
                 ///进入当前应用程序域。 .NET框架在我们允许使用的调用上锁定我们
                 ///当加载这些程序集时,所以我们需要依赖于AssemblyResolver实例附加的
                 /// AppDomain为了加载我们想要的方式。
        /// </remarks>
        public IList<Assembly> LoadAssemblyWithReferences(LoadMethod loadMethod, string assemblyPath)
        {
            var list = new List<Assembly>();
            var assembly = this.LoadAssembly(loadMethod, assemblyPath);
            list.Add(assembly);

            foreach (var reference in assembly.GetReferencedAssemblies())
            {
                list.Add(Assembly.Load(reference));
            }

            return list;
        }

        /// <inheritdoc />
        /// <remarks>
        /// Just a simple call to AppDomain.CurrentDomain.GetAssemblies(), nothing more.
        /// </remarks>
        public Assembly[] GetAssemblies()
        {
            return AppDomain.CurrentDomain.GetAssemblies();
        }

        #endregion
    }
}

四.总结:

 
 本文首要讲明了动用程序域的有关概念,本种类重大讲明.NET对象的跨应用程序域的传递,由于设计使用程序域的内容,所以本文首要教师了部分基本概念,以致部分骨干的靶子,对于利用程序域包涵的次序集的有关内容将要下边进行操作。在实际的花色中,超级少直接取操作使用程序域,相当多的是一向操作程序集,所以在本文的末梢交给了三个就暗淡的前后相继集的操作方法。

相关文章

admin

网站地图xml地图