?
Ted Pattison
?
代码下载位置: OfficeSpace2008_03.exe (762 KB)
Browse the Code Online
?
我 从 2005 年夏天开始开发 Windows? SharePoint? Services (WSS) 3.0 和 Microsoft? Office SharePoint Server (MOSS) 2007。从那时起,我已经创建了数以百计的新类库项目并手动对其进行了配置,以开发各种类型的 SharePoint 组件,如“功能”、“应用程序页面”、“页面模板”和“Web 部件”等。
不久以后,我又开始标准化解决方案中的文件和文件夹的公用项目结构,这些解决方案都是我使用面向 SharePoint 开发的 Visual Studio? 2005 创建的。但是,这是一个痛苦的过程:对于每个项目,仅创建作为起点的基本结构就要花费五到十分钟的时间。而各项目之间除项目名不同以外,源文件几乎完全相同。 许多开发人员在进行 SharePoint 开发时还会面临另一个艰巨问题,那就是创建必需的文件以生成测试和部署时所需的解决方案包文件 (.wsp)。与许多其他 SharePoint 开发人员一样,我也习惯了手动创建和更新 manifest.xml 文件和 .ddf 文件来生成自己的解决方案包。 在过去的一年中,我一直在与其他一些 SharePoint MVP(包括 John Holliday、Andrew Connell 以及 Scot Hillier 等)合作,为实现同一个目标他们都提出了非常相似但又略有不同的架构。我们讨论了有关设计和实现代码生成器的构思,它可以为新的 Visual Studio 项目和解决方案自动产生所需的全部源文件。最终目标当然是为启动和运行新的 SharePoint 开发项目提供更快的方法。 在创建了可生成具有公用文件夹结构的每个新 Visual Studio 解决方案的代码生成器后,才有可能编写所需的代码来重新生成产生解决方案包 .wsp 文件所需的 manifest.xml 文件和 .ddf 文件。这样就避免了手动修改 manifest.xml 或 .ddf 文件的需要。 本月专栏将介绍一个名为 STSDEV.EXE 的概念验证应用程序,此程序演示了如何为 SharePoint 开发创建简单的代码生成器。STSDEV 是一个控制台应用程序,它非常便于开发 MOSS 2007 的组件。另外,它能够以可重复的方式生成代码,这就使得为遵循最佳实践的 SharePoint 开发生成 Visual Studio 解决方案成为可能。 在本月专栏中,我的目标是讨论如何使用 STSDEV 实用程序创建和扩展 Visual Studio 项目,为 SharePoint 开发创造有效灵活的环境。此处所概述的方法非常灵活,因为它既可以用于 Visual Studio 2005,也可以用于 Visual Studio 2008。而且,当需要从一个版本的 Visual Studio 移动到另一个版本时,此方法还非常容易迁移。?
RootFiles 目录 要想了解 SharePoint 开发中有关部署的工作原理,其中一个重要方面就是要知道需要将模板文件复制到前端 Web 服务器上的哪个位置。大多数文件都必须部署到各个前端 Web 服务器上一个名为 SharePoint RootFiles 的目录内,在 WSS 3.0 中此目录位于:C:\Program Files\Common Files\Microsoft Shared\web server extensions\12请看一下图 1,查看在 RootFiles 目录中嵌套的子目录。在绝大多数情况下,需要部署自定义模板文件的目录都是 TEMPLATE 目录。但有时也需要将文件部署到其他一些子目录中,如 ISAPI 目录(部署自定义 Web 服务时)和 Resources 目录(需要部署具有全局资源的 .resx 文件时)。
public interface ISolutionProvider { string Title { get; } void InitializeSolution(); void AddSolutionItems(); }? 此设计的目的旨在使高级 SharePoint 开发人员能够通过创建其自己的解决方案提供程序来轻松扩展 STSDEV,然后这些提供程序可以为特殊方案生成 Visual Studio 项目,或用来满足特殊公司的标准和原则。 但是,使用 STSDEV 创建的每个解决方案和项目会始终共享公用结构和用于生成输出解决方案包的标准部署文件集。STSDEV 实用程序还会创建具有标准配置集和自定义生成目标的各个 Visual Studio 解决方案,允许开发人员自动化在开发期间需要用到的许多操作,如安装、部署或收回当前项目的解决方案包。 为了得到更好的方法,让我们检查一下使用空解决方案提供程序生成的 HelloWorld 解决方案的结构。如图 3 所示,STSDEV 生成一个名为 HelloWorld.sln 的解决方案文件和一个名为 HelloWorld.csproj 的项目文件,这可以使用 Visual Studio 2005 解决方案格式或 Visual Studio 2008 解决方案格式来创建。另外还要注意,STSDEV 实用程序使用标准类库项目类型创建 Visual Studio 项目,这会有利于将 Visual Studio 2005 项目升级到 Visual Studio 2008。
<Solution SolutionId="24F91DED-8BA7-4633-8BA0-4C9B2A4387D7" ResetWebServer="True" xmlns="http://schemas.microsoft.com/sharepoint/" > </Solution>?同样,SolutionPackage.ddf 文件如下所示:
; Generated by STSDEV at 1/7/2008 8:42:21 AM .OPTION EXPLICIT .Set CabinetNameTemplate=HelloWorld.wsp .set DiskDirectoryTemplate=CDROM .Set CompressionType=MSZIP .Set UniqueFiles=off .Set Cabinet=on .Set DiskDirectory1=DeploymentFiles DeploymentFiles\manifest.xml?但是,如果在 RootFiles 文件中添加一些新文件夹然后将一个图像文件(如 AfricanPith32.gif)复制到其中,会发生什么情况呢?下次运行 Visual Studio 生成命令时,会有一个自定义生成目标调用 STSDEV 实用程序并执行 RefreshDeploymentFiles 命令。manifest.xml 随即被更新,如下所示:
<Solution SolutionId="24F91DED-8BA7-4633-8BA0-4C9B2A4387D7" ResetWebServer="True" xmlns="http://schemas.microsoft.com/sharepoint/" > <!--TEMPLATE files--> <TemplateFiles> <TemplateFile Location="IMAGES\HelloWorld\AfricanPith32.gif" /> </TemplateFiles> </Solution>?SolutionPackage.ddf 文件将被更新以包括:
... ;*** Solution manifest DeploymentFiles\manifest.xml .Set DestinationDir=IMAGES\HelloWorld RootFiles\TEMPLATE\IMAGES\HelloWorld\AfricanPith32.gif? 尽管 RefreshDeploymentFiles 命令能够确定要添加到 manifest.xml 和 SolutionPackage.ddf 的文件,但仍需跟踪有关解决方案自身的其他元数据以及任何应在解决方案包内部署的程序集。因此,STSDEV 在最初就被设计为生成一个名为 SolutionConfig.xml 的 XML 元数据文件,并在重新生成 manifest.xml 和 SolutionPackage.ddf 的时候读取此文件的内容。 SolutionConfig.xml 文件的最基本版本如下所示:
<Solution> <SolutionId>24F91DED-8BA7-4633-8BA0-4C9B2A4387D7</SolutionId> <SolutionName>HelloWorld</SolutionName> <ResetWebServer>True</ResetWebServer> <AssemblyDeployment>False</AssemblyDeployment> </Solution>? 如您所见,SolutionConfig.xml 文件将跟踪解决方案 ID 的 GUID、解决方案名称以及一个用于在 manifest.xml 中添加指令的名为 ResetWebServer 的布尔值,它可以控制在部署、升级或收回输出解决方案包后,WSS 是否在每个前端 Web 服务器上运行 IISRESET。 SolutionConfig.xml 还包含一个名为 AssemblyDeployment 的元素,此元素可通过 RefreshDeploymentFiles 命令读取。利用它可添加有关一个或多个需要在输出解决方案包中部署的程序集的配置以及所需的程序集配置信息。 例如,如果通过选择空解决方案 (C# assembly) 创建了 HelloWorld 项目,则 SolutionConfig.xml 文件的初始状态应如图 4 所示。二进制文件和元数据信息随后将被添加到 manifest.xml 和 SolutionPackage.ddf 中用于进行程序集部署。 Figure 4 Deploying an Assembly with SolutionConfig.xml
<!-- SolutionConfig.xml file for an Empty Solution with C# assembly --> <Solution> <SolutionId>24F91DED-8BA7-4633-8BA0-4C9B2A4387D7</SolutionId> <SolutionName>HelloWorld</SolutionName> <ResetWebServer>True</ResetWebServer> <AssemblyDeployment>True</AssemblyDeployment> <!--Assembly files--> <Assemblies> <Assembly Location="HelloWorld.dll" DeploymentTarget="GlobalAssemblyCache"> <SafeControls> <SafeControl Assembly="HelloWorld, [full 4-part assembly name]" Namespace="HelloWorld" TypeName="*" Safe="True" /> </SafeControls> </Assembly> </Assemblies> </Solution>? 使用 MSBuild 创建自定义生成目标 STSDEV 并不使用批处理文件,而是充分利用 MSBuild 的功能来创建可集成到 Visual Studio 解决方案中的自定义生成目标。特别要指出的是,STSDEV 将在包含自定义生成目标的 DeploymentFiles 文件夹中创建一个名为 Microsoft.SharePoint.targets 的文件。这就使得利用在 SharePoint 开发中通用的完整命令集来创建每个新解决方案成为可能。图 5 提供了为每个新项目生成的自定义生成目标的列表。 Figure 5 Custom Build Targets
<?xml version="1.0" encoding="utf-8" ?> <Project DefaultTargets="DebugBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <PackageName>HelloWorld.wsp</PackageName> <PackageFile>HelloWorld.wsp</PackageFile> <WssRootFilesFolder> <!--RootFiles path--> </WssRootFilesFolder> <MAKECAB> <!-- MAKECAB.EXE path--> </MAKECAB> <STSADM> <!-- STSADM.EXE path--> </STSADM> <STSDEV> <!—STSDEV.EXE path--> </STSDEV> </PropertyGroup> <Target Name="DebugBuild"> <!—custom build target 1 --> </Target> <Target Name="DebugInstall" DependsOnTargets="DebugBuild"> <!—custom build target 2 --> </Target> </Project>? 请注意,Microsoft.SharePoint.targets 顶部的 PropertyGroup 部分用于跟踪元数据和值(如单个目标命令中所需文件的路径和名称等)。您可以看到 Microsoft.SharePoint.target 将跟踪输出解决方案包的文件名以及实用程序的完全限定文件路径,如 MAKECAB 和 STSADM。此外还有一个属性,它可跟踪 STSDEV 自身的本地文件路径,以便命令目标能够调用 STSDEV 实用程序并执行 RefreshDeploymentFiles 命令。 现在就让我们来看一下几个生成目标。首先我将检查在名为 DebugBuild 的生成目标中定义的命令,如图 7 所示。 Figure 7 DebugBuild Target
<Target Name="DebugBuild"> <Message Text="Refreshing Deployment Files..." Importance="high" /> <Exec Command="$(STSDEV) /refresh $(TargetName) $(SolutionDir)" ContinueOnError="false" /> <Message Text="Deleting Solution Package File..." Importance="high" /> <Delete Files="$(ProjectDeploymentFilesFolder)\$(PackageFile)" ContinueOnError="true" /> <Message Text="Building Solution Package (Debug Version)" Importance="high" /> <Exec Command="$(MAKECAB) /F $(ProjectDeploymentFilesFolder)\SolutionPackage.ddf /D CabinetNameTemplate=$(PackageFile)" ContinueOnError="false" /> </Target>? 如您所见,DebugBuild 目标中的第一个命令将调用 STSDEV 并传递 /refresh 参数以运行 RefreshDeploymentFiles 命令。这将导致 STSDEV 实用程序重新生成 manifest.xml 和 SoutionPackage.ddf 作为解决方案包生成过程的第一部分。随后执行一个 MSBuild 命令来删除任何现有的 .wsp 文件,并使用 MAKECAB.EXE 实用程序重新生成解决方案包。还应注意 Message 命令的使用,它可用于在命令运行时,通过 Visual Studio 输出窗口向开发人员显示信息性消息。 所编写的 DebugInstall 命令可使用 STSADM 提供的 addsolution 操作将新生成的解决方案包 .wsp 文件安装到本地场的解决方案存储区中。还要注意 DependsOnTargets 属性的使用,它会在每次执行 DebugInstall 目标时首先运行 DebugBuild 目标命令:
<Target Name="DebugInstall" DependsOnTargets="DebugBuild"> <Message Text="Installing Solution..." Importance="high" /> <Exec Command="$(STSADM) -o addsolution -filename $(ProjectDeploymentFilesFolder)\$(PackageFile)" ContinueOnError="true" /> <Exec Command="$(STSADM) -o execadmsvcjobs" /> <Message Text="" Importance="high" /> </Target>?STSADM 实用程序所展示的许多操作都使用计时器作业异步运行。因此,还有另外一个对 STSADM 的调用,它运行 execadmsvcjobs 操作。此操作可使命令暂停并等待,直到本地 SharePoint Web 服务器能够运行完所有挂起的计时器作业为止。在调用 deploysolution 操作后,还会立即在 DebugDeploy 目标中调用 execadmsvcjobs 操作:
<Target Name="DebugDeploy" DependsOnTargets="DebugInstall"> <Message Text="Deploying Solution..." Importance="high" /> <Exec Command="$(STSADM) -o deploysolution -name $(PackageName) -local -allowGacDeployment -force" /> <Exec Command="$(STSADM) -o execadmsvcjobs" /> <Message Text="" Importance="high" /> </Target>?现在您已经看到在由 STSDEV 实用程序生成的 Microsoft.SharePoint.targets 文件中存在的不同自定义生成目标,接下来您需要了解如何在 Visual Studio 环境中执行它们。这其实非常简单。您只需选择与所需的生成目标相关联的 Visual Studio 配置,然后运行标准 Visual Studio 生成命令即可,如图 8 所示。 ?
<Target Name="AfterBuild"> <CallTarget Targets="DebugBuild" Condition="'$(Configuration)|$(Platform)' == 'DebugBuild|AnyCPU'"/> <CallTarget Targets="DebugInstall" Condition="'$(Configuration)|$(Platform)' == 'DebugInstall|AnyCPU'"/> <CallTarget Targets="DebugDeploy" Condition="'$(Configuration)|$(Platform)' == 'DebugDeploy|AnyCPU'"/> <CallTarget Targets="DebugRedeploy" Condition="'$(Configuration)|$(Platform)' == 'DebugRedeploy|AnyCPU'"/> <CallTarget Targets="DebugUpgrade" Condition="'$(Configuration)|$(Platform)' == 'DebugUpgrade|AnyCPU'"/> <CallTarget Targets="DebugQuickCopy" Condition="'$(Configuration)|$(Platform)' == 'DebugQuickCopy|AnyCPU'"/> <CallTarget Targets="DebugRetract" Condition="'$(Configuration)|$(Platform)' == 'DebugRetract|AnyCPU'"/> <CallTarget Targets="DebugDelete" Condition="'$(Configuration)|$(Platform)' == 'DebugDelete|AnyCPU'"/> <CallTarget Targets="ReleaseBuild" Condition="'$(Configuration)|$(Platform)' == 'ReleaseBuild|AnyCPU'"/> </Target>? 您会注意到,Target 部分的名称为 AfterBuild。这是一个众所周知的目标名称,在完成每个生成后 Visual Studio 都会自动运行它。这将便于您为每个生成目标添加一个 CallTarget 命令以及某个条件,以根据所选的配置执行相应的自定义生成目标。
请将您想向 Ted 询问的问题和提出的意见发送至 mmoffice@microsoft.com.