在目前的项目开发中,分布式开发已经逐渐成为主流。一个项目要是没有采用分布式架构,都不好意思跟别人说这是一个完整的项目。这句话虽然有些过激,但是随着人们对效率的要求在提高,以及产品需要提升用户体验。只有在软件项目的效率和体验做到高质量,才可以赢得用户和市场。

对于.NET项目,我们使用较多的分布式结构有Webservice,.Net remoting,MSMQ,WCF,WebAPI等等,我们在使用这些框架的时候,从这些分布式框架中得到了很好的用户体验。在.NET项目中,分布式架构对项目的开发也有很大的效率提升。

很多人会问,这些分布式框架的底层原理是什么呢?恐怕谁也不敢轻言几句就可以描述完毕,在这个博文系列中,就是简单的描述一下这些分布式结构的底层实现原理。

本文主要讲解对象在应用程序域中的传递。主要讲解应用程序域的一些核心对象,对于应用程序域的操作出现的比较少,所以在这里给出的是程序集的一些基本操作。如有不足之处,还望多多指正。

一.AppDomain解析:

AppDomain在很多场合都是被翻译为“应用程序域”,在本文中也将采用这一翻译。对于.NET的开发者,对于CLR应该是最熟悉不过了,CLR类似于java的JVM。在CLR中,AppDomain规定了代码的执行范围,提供了错误隔离的程度,提供了一个安全隔离度,并且拥有自己的资源。AppDomain的具体功能,有如下图:

1.AppDomain概述:

AppDomain类似与系统的进程,进程是有操作系统进行创建,AppDomain是由CLR进行创建。一个给定的AppDomain必须驻留在一个操作系统的进程中,而一个给定的进程可以寄宿多个AppDomain。有如下图:

如上图所示,一个对象正好存放在一个AppDomain种,值也一样。一个AppDomain中的对象引用必须是引用同一AppDomain中的对象,AppDomain的行为就好像拥有自己私有的地址空间。如果两个AppDomain需要使用一个类型,必须为每个AppDomain分别初始化和分配一次类型。必须为各个用到类型的AppDomain分别加载和初始化一次类型的方法和程序集。进程种的各个AppDomain要维护类型的不同拷贝。对于类型的静态子类,每个AppDomain都有其自己的私有副本。

AppDomain的资源有如图:

对于应用AppDomain的资源被加载,一直在内存中,卸载AppDomain资源是唯一卸载模块或者程序集的途径,卸载AppDomain资源也是回收类型静态字段所占内存的唯一方式。

在上面提到过操作系统的线程与AppDomain类似,在CLR中定义了System.Threading.Thread,在AppDomain中表示为可调度的实体,在这里提出一个新的概念,那就是“软线程”和“硬线程”,顾名思义,操作系统的线程被称为“硬线程”,CLR中的System.Threading.Thread被称为“软线程”。一个CLR软线程对象驻留在一个确定的AppDomain中;一个给定的AppDomain可能有多个软线程对象。在当前的CLR中,对于给定的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():为指定名称获取存储在当前应用程序域中的值。

[SecuritySafeCritical]
public object GetData(string name)
{
if (name == null)
throw new ArgumentNullException("name");
switch (AppDomainSetup.Locate(name))
{
case -:
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[] != null)
((IPermission) objArray[]).Demand();
return objArray[];
case :
return (object) this.FusionStore.ApplicationBase;
case :
return (object) this.FusionStore.ConfigurationFile;
case :
return (object) this.FusionStore.DynamicBase;
case :
return (object) this.FusionStore.DeveloperPath;
case :
return (object) this.FusionStore.ApplicationName;
case :
return (object) this.FusionStore.PrivateBinPath;
case :
return (object) this.FusionStore.PrivateBinPathProbe;
case :
return (object) this.FusionStore.ShadowCopyDirectories;
case :
return (object) this.FusionStore.ShadowCopyFiles;
case :
return (object) this.FusionStore.CachePath;
case :
return (object) this.FusionStore.LicenseFile;
case :
return (object) (bool) (this.FusionStore.DisallowPublisherPolicy ? : );
case :
return (object) (bool) (this.FusionStore.DisallowCodeDownload ? : );
case :
return (object) (bool) (this.FusionStore.DisallowBindingRedirects ? : );
case :
return (object) (bool) (this.FusionStore.DisallowApplicationBaseProbing ? : );
case :
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[];
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对象的跨应用程序域的传递,由于设计应用程序域的内容,所以本文主要讲解了一些基本概念,以及一些基本的对象,对于应用程序域包含的程序集的相关内容将在下面进行操作。在实际的项目中,很少直接取操作应用程序域,比较多的是直接操作程序集,所以在本文的最后给出了一个就暗淡的程序集的操作方法。

解析.NET对象的跨应用程序域访问(上篇)的更多相关文章

  1. 解析.NET对象的跨应用程序域访问(下篇)

    转眼就到了元宵节,匆匆忙忙的脚步是我们在为生活奋斗的写照,新的一年,我们应该努力让自己有不一样的生活和追求.生命不息,奋斗不止.在上篇博文中主要介绍了.NET的AppDomain的相关信息,在本篇博文 ...

  2. 解析.NET对象的跨应用程序域访问--AppDomain(上篇)

    在目前的项目开发中,分布式开发已经逐渐成为主流.一个项目要是没有采用分布式架构,都不好意思跟别人说这是一个完整的项目.这句话虽然有些过激,但是随着人们对效率的要求在提高,以及产品需要提升用户体验.只有 ...

  3. NET对象的跨应用程序域

    NET对象的跨应用程序域 转眼就到了元宵节,匆匆忙忙的脚步是我们在为生活奋斗的写照,新的一年,我们应该努力让自己有不一样的生活和追求.生命不息,奋斗不止.在上篇博文中主要介绍了.NET的AppDoma ...

  4. WPF 中那些可跨线程访问的 DispatcherObject(WPF Free Threaded Dispatcher Object)

    原文 WPF 中那些可跨线程访问的 DispatcherObject(WPF Free Threaded Dispatcher Object) 众所周知的,WPF 中多数对象都继承自 Dispatch ...

  5. 对象存储 COS 帮您轻松搞定跨域访问需求

    背景 早期为了避免 CSRF(跨站请求伪造) 攻击,浏览器引入了 "同源策略" 机制.如果两个 URL 的协议,主机名(域名/IP),端口号一致,则视为这两个 URL " ...

  6. .NET跨AppDomain访问对象

    什么是AppDomain? 我们都知道windows进程,它起到应用程序隔离的作用,带来的好处是,当某个进程发生错误的时候,不会影响其他的进程,系统也不会受到影响.但是,创建windows进程的代价是 ...

  7. Chrome浏览器扩展开发系列之十五:跨域访问的XMLHttpRequest对象

    XMLHttpRequest对象是W3C的标准API,用于访问服务器资源.XMLHttpRequest对象支持多种文本格式,如XML和JSON等.XMLHttpRequest对象可以通过HTTP和HT ...

  8. 解决Entity Framework查询匿名对象后的跨域访问的一种方式

    在Entity Framework中,可以使用lambda表达式进行对数据的查询,而且可以将查询结果直接映射为对象或者对象列表,这极大的提高的开发速度,并且使数据层的数据更加方便处理和传递.但是很多时 ...

  9. Asp.Net SignalR 使用记录 技术回炉重造-总纲 动态类型dynamic转换为特定类型T的方案 通过对象方法获取委托_C#反射获取委托_ .net core入门-跨域访问配置

    Asp.Net SignalR 使用记录   工作上遇到一个推送消息的功能的实现.本着面向百度编程的思想.网上百度了一大堆.主要的实现方式是原生的WebSocket,和SignalR,再次写一个关于A ...

随机推荐

  1. MAC终端如何使用rar和unrar

    一.MAC具体安装见下面两个博客分享: Homebrew介绍和使用:https://www.jianshu.com/p/de6f1d2d37bf Mac 压缩 / 解压缩工具解决方案:https:// ...

  2. Java往指定地址接口发送内容方法

    package com.upload.tool; import java.io.BufferedReader;import java.io.InputStreamReader;import java. ...

  3. 思科模拟器GNS3-2.1.8安装笔记 (适用于版本2.0.3以上的GNS3)

    当前现阶段学习经常使用的路由交换设备主要来自于思科.华为和华三三家,这三家的设备操作配置大致类似,却又不尽相同.因为实体设备通常都非常昂贵,所以作为学习,我们通常会使用它们提供的模拟器.华为的模拟器是 ...

  4. Failed to create agent because it is not close enough to the NavMesh

    主要原因是:两个相同对象navmesh点太近造成. 解决方案:通过NavMesh.SamplePosition 获得可以行走点 ;i<;i++) { , mRadius); , 3.14f); ...

  5. 博客六--Tensorflow卷积神经网络的自主搭建

    本人较懒也很忙,所以就不重复工作.连接我的开源中国博客查询:https://my.oschina.net/u/3770644/blog/3042523

  6. 2017沈阳站 Tree

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6228 Tree Time Limit: 2000/1000 MS (Java/Others)    M ...

  7. flex布局嵌套之高度自适应

    查遍各大资源无任何flex嵌套布局的例子,经过自己折腾完成了项目中的高度自适应需求(更多应用于前端组件) 效果图: html代码:(关键地方已经用颜色特别标识 ^_^) <!DOCTYPE ht ...

  8. 【机器学习】支持向量机(SVM)的优化算法——序列最小优化算法(SMO)概述

    SMO算法是一一种启发式算法,它的基本思路是如果所有变量的解的条件都满足最优化问题的KKT条件,那么这个最优化问题的解就得到了.因为KKT条件是该优化问题的充分必要条件. 整个SMO算法包括两个部分: ...

  9. JQUERY-事件-动画-类数组对象-添加自定义API

    正课: 1. 事件: 2. 动画: 3. 类数组对象操作: 4. 添加自定义API: 1. 事件: 1. 页面加载后执行: 2个时机 1. DOM内容加载完成: 仅包括html, js DOMCont ...

  10. (PMP)第2章-----项目运行环境

    1.事业环境因素:(客观存在,可能有帮助或阻碍,项目经理必须遵守) 内部:文化,结构,治理:设施和资源的地理分布,基础设施,信息技术软件,资源可用性,员工能力 外部:市场条件,社会和文化,法律限制,商 ...