上一篇我们看了场解决方案与沙盒方案两种执行模型,其中场解决方案包括有完全信任方式与Bin/CAS方式两种,这里让我们继续来看看最后一个执行模型,即混合模型(或混合模式)。
三、混合模式(hybrid approaches)
所谓混合模式就是指虽然解决方案运行在沙盒模式,但却可以通过各种机制来访问完全信任模式的代码,换句话说,就是把沙盒模式与完全信任模式结合起来。当然,我们可以把这种模式看成两个松耦合方式的组合,这是因为沙盒模式是部署在网站集(Site Collection)的解决方案库(Solution Gallery),而完全信任解决方案却是部署到服务器场,它们的开发可以是孤立的,非同时进行的,并且完全信任模式的组件可以被一个或多个沙盒模式组件使用。通常情况下,完全信任模式的解决方案需要通过IT中心团队进行集中开发和掌控并提供给沙盒模式的解决方案使用。
沙盒模式可以使用三种类型的完全信任组件:
- 完全信任代理(Full trust proxies). 通过编写一个继承自SPProxyOperation抽象类的自定类,在此类中实现完全信任模式下的代码功能,然后把它的程序集部署到GAC(global assembly cache),通过此方式把完全信任代理功能暴露给沙盒以沙盒代码调用。
- 外部内容类型(External content types).你可以通过外部内容类型来从LOB应用程序(line-of-business application)以及其它通过BCS连接的外部数据源获取外部数据。外部内容类型必须以完全信任模式进行部署。不管怎样,你可以在沙盒环境内通过创建外部列表(External Lists)来获取通过沙盒方式提取不到的数据。
- 用户自定义的工作流活动(Custom workflow activities). 通过创建用户自定义的以编码实现的工作流活动,并且把它以完全信任模式部署到GAC(global assembly cache),然后就可以在沙盒里调用这个工作流活动了。
接下来我们分别看看上述三种类型的实现细节。
1、完全信任代理(Full trust proxies).
Hybrid execution using a full-trust proxy
当在沙盒中使用完全信任代理时,代码访问安全 (CAS) 策略会允许沙盒代码访问完全信任代理的程序集,你可以通过在Feature Receiver中编程来向Farm中注册代理程序集,也可以通过Window PowerShell工具来注册代理程序集。
你的沙盒代码必须使用SPProxyOperationsArgs类来构造需要传递给完全信任代理的参数。当你调用 SPUtility.ExecuteRegisteredProxyOperation方法时,沙盒工作进程会激活完全信任代理并把相关参数传递进去,然后完全信任代理的代码会在完全信任方式下运行,在完成其负责的处理任务后,完全信任代理会返回相关参数(如果有的话)给沙盒解决方案,并继续由沙盒方案完成其它任务。
需要注意的是,Sharepoint的上下文(SPContext)不适用于代理操作类,也就是说,如果你想要在代理操作类中使用上下文相关信息,你就需要传递创建上下文对象所需要的参数进入代理类。比如:如果你需要在代理中存取某个Site,那么你就需要传递Site ID参数进入代理类,然后代理类会在它的执行代码中使用这引Site ID来构建一个site,接下来你就可以使用site.RootWeb.CurrentUser来获取SPUser对象及其信息了。
下图表示这种处理方式的主要工作逻辑与重要组件
- SPProxyOperation. 这个类提供了一个抽象类给完全信任代理。此类包含了一个叫Execute的方法,在这个方法中你可以定义你需要完成的完全信息代理操作。它的完全信任代理类需要部署到GAC(global assembly cache)并需要使用我们上面提到的方法注册到Sharepoin场中。
- SPProxyOperationArgs. 这个类提供了一个抽象的基类以供你创建传递给完全信任代理类的参数,你必须创建一个可序列化的类,此类继承自SPProxyOperationArgs ,在此类中添加属性来实现参数的设置与获取。
- SPUtility.ExecuteRegisteredProxyOperation.这个静态方法用于让你在沙盒代码中激活完全信任代理。这个方法需要一个程序集名,一个类型名以及一个SPProxyOperationArgs对象。此方法返回一个对象类型的变元(an argument of type Object)给它的调用者。
需要注意的是:任何传递给代理类的参数类(proxy arguments class)中的所定义的类型都必须被标记为serializable,而在代理操作中返回的类型也需要被标记为serializable,这样做的目的是在进程之间进行信息传递。
不管是代理中的操作(operation)还是代理类的参数类都需要被部署到GAC。你不能把在沙盒代码里定义的类型传递给代理,这是因为代理并不会访问你所加载的沙盒程序集。
2、外部内容类型(External content types)
外部内容类型为可重用的元数据集合,其中包含连接信息和数据定义以及要应用于特定外部数据类别的行为。创建外部内容类型通常是使用 Microsoft Business Connectivity Services (BCS) 将外部数据源中的数据添加到 SharePoint 2010中的第一步。
如果你想要在沙盒解决方案中使用外部数据类型,那么你就只能通过混合方式来实现了。
外部内容类型(External content types)必须在场级别(Farm-scoped)的Feature中定义,它不能作为沙盒方案的一部分进行部署。
为了简化外部内容类型的创建,Business Connectivity Services 提供了两种开发工具:Microsoft SharePoint Designer 2010 中的外部内容类型设计器和 Microsoft Visual Studio 2010 中的 Business Connectivity Services Model Designer。
如果您的外部数据源具有 Windows Communication Foundation (WCF)、Web 服务或基于 .NET Framework 程序集的集成服务,或为简单 Microsoft SQL Server 数据库,则可使用 SharePoint Designer 中的外部内容类型设计器来发现您的外部系统,并创建外部内容类型,而无需编写代码或 XML。但是,如果您的外部系统很复杂或不受 SharePoint Designer 支持,则可以使用 Visual Studio(提供 Intellisense)中的 XML 编辑器来创建 XML 模型,或使用 Visual Studio外部内容类型设计器来创建 .NET 连接程序集(该程序集通过 .NET Framework 代码提供自定义后端集成逻辑)。
在成功创建并部署好外部内容类型后,你就可以在沙盒方案中基于外部内容类型创建外部数据列表了(External List),并通过外部数据列表(External List)来获取外部数据。
Hybrid Execution with external content types
我们可以通过使用外部数据列表(External List)的SPList对象模型来在沙盒中使用代码获取外部数据。我们不能在沙盒代码中直接使用BCS运行时的APIs来达到获取外部数据的目的。
Hybrid execution using an external list
当在沙盒中获取外部数据时,我们必须要面对安全方面的问题。
通过前面的描述我们知道,沙盒方案通过外部数据列表(External List)获取外部数据,外部数据列表会调用BCS运行时APIs来完成对外部数据的获取,而与外部数据打交道的相关任务应该是在用户代码代理服务(user code proxy service )中执行。出于安全的考虑,当上下文(Context)进入沙盒工作进程时,Sharepoint会移除与上下文绑定的用户凭证。因此,与此用户相关联的Windows Identity就不再会作用于沙盒工作进程与沙盒代理进程,由于用户的Windows Identity不再起作用, Sharepoint的Managed Account就成为了沙盒通过BCS访问外部数据或外部服务的基础,所有的沙盒用户都将会基于Sharepoint的Managed Account来运行用户代码代理服务(user code proxy service)。
当BDC(Business Data Connectivity (BDC) Service)运行时接收到请求外部数据的请求时,它会判断安全存储服务(Secure Store Service (SSS))是否会参与安全凭证的管理, 我们知道安全存储服务(Secure Store Service (SSS))可安全地为外部系统存储凭证集并将这些凭证集与个人或组标识相关联。
在这里如果SSS要参与,那么与此请求相关联的用户标识就会被提供给SSS,SSS负责把此用户标识与外部系统凭证集中的某个凭证进行关联,然后SSS会向BDC运行时返回映射后的那个外部系统凭证,BDC就会使用这个映射凭证去访问外部数据。由于BDC不会接收单个用户的凭证(credentials of individual users),所以你无法约束特定的用户是否能从沙盒中访问外部数据。
下面的图演示了这样一个处理过程。
下面我们说说它的各个步骤:
- 沙盒中的用户代码(User Code)通过使用SPList对象模型访问外部数据,当沙盒向用户代码代理服务(User Code Proxy Service)提交请求的时候,用户的身份令牌便会从上下文(Context)中移除。
- 对SPList对象模型的访问请求会被委托给用户代码代理服务(User Code Proxy Service),由它把数据请求传递给BDC运行时,由上图可见,BDC运行时(BDC Runtime)也运行在用户代码代理服务进程中(User code proxy service process)。
- 接下来,BDC运行时会调用安全存储服务(Secure Store Service (SSS)),此时与这个数据请求(Request)绑定的身份标识就是运行用户代码代理服务所绑定的Sharepoint的managed account。BDC所请求的外部数据系统(External System)的凭证也会被映射到用户代码代理服务(user code proxy service)中的某个标识上, SSS返回给BDC相关凭证。
- BDC运行时从BDC 元数据缓存中获取外部内容类型(如果元数据不在缓存中,则BDC运行时会直接从BDC服务中去获取)。依靠外部内容类型的元数据(external content type metadata),BDC才能知道如何与外部数据系统进行交互。
- BDC运行时使用SSS返回给它的那个标识(上面第三步从SSS返回给BDC的标识)去访问外部数据系统,获取请求所需要的数据。
3、用户自定义的工作流活动(Custom workflow activities)
Hybrid execution using a declarative workflow
通过这个方法,包含在用户自定义工作流活动中的完全信任代码可以被沙盒工作进程激活,并以异步方式运行,从而达到沙盒与完全信任两种方式混合的目的。
沙盒环境允许你部署工作流,在工作流中可以包含若干个工作流活动,Sharepoint提供了许多开箱即用的工作流活动,我们也可以开发自定义的工作流活动并以完全信任方式部署到GAC(global assembly cache)中提供给沙盒使用。
为了创建完全信任工作流活动,我们需要设置Web.config文件,在里面加入authorizedType。这样就使得你开发的自定义工作流活动具备和Sharepoint那些开箱即用的工作流活动一样的状态。下面的代码就是authorizedType的设置示意 :
<configuration>
<System.Workflow.ComponentModel.WorkflowCompiler>
<authorizedType Assembly="…" Namespace="…" TypeName="*" Authorized="True" />
</System.Workflow.ComponentModel.WorkflowCompiler>
</system.web>
当你加入一个授权类型时,Assembly属性就是你的程序集的强名称,Namespace属性就是活动类所用的完全限定名称空间。
你可以在沙盒内创建与部署工作流活动,这些工作流活动受到与沙盒一样的约束,如果你想要突破这种约束,你就只能开发与部署完全信任方式的工作流活动。 需要注意的是,工作流引擎自身总是运行在完全信任模式下的,当你部署了一个沙盒中的工作流活动时,工作流引擎就会在完全信任模式的基础上加入特定的约束规则从而附合沙盒的约束。
下面的图示意了工作流是如何执行的,那个declarative workflow工作流从沙盒方案中加载,而那个用户自定义的完全信任工作流活动则从GAC缓存(global assembly cache)中加载,。
Hybrid execution with a custom workflow activity
Declarative workflow虽然是作为沙盒方案的一部分定义的,但它却是在Owstimer.exe, W3wp.exe或user code proxy process.等进程中运行的,也就是说,它是由运行在完全信任模式中的进程执行的。Declarative workflow不能在SharePoint Foundation 与 SharePoint Server之间移动,,如果必须,你只能在两种环境中各创建一个等值的Declarative workflow,当然,在SharePoint Server中提供的工作流活动要多一些,并且Declarative workflow的管理也与SharePoint Foundation略有不同,但在打包与部署方面,二者是相同的。作为一个好的习惯,建议你用于开发工作流的Sharepoint版本与生产机保持一致,这样可以尽量减少后期不必要的麻烦。