这篇文章是从我的 github 博客 http://lxconan.github.io 导入的。

这是这个系列的第五篇了,前四篇请参见:

简单来说,部署就是 “构建(Build)” -> “拷贝(打包)” -> “配置”。在前一篇中,我们介绍了“构建”,那么这一篇就说说拷贝(好像我们更习惯于说打包,那么以后我们就叫它打包吧)的事情。为什么要打包呢?在应用程序发布的时候我们当然只希望发布运行时需要的文件,而其他的文件,例如:工程文件,源代码等等是不需要进行发布的。因此我们需要将运行时所需的文件分离出来,做成一个干净的 Package。

打包 - 思路

只需要解决楚两个问题,打包就完成了:第一个问题是,我们打的包应该有怎样的目录结构;第二个问题是,应该拷贝哪些文件夹到包的哪些目录里去。

应该拷贝哪些文件

在回答第一个问题之前,我们先来看看有哪些文件需要进行拷贝。构建好的程序集(.dll 和 .exe)需要拷贝,没错,但是除了它们以外还有其他文件需要进行拷贝。如果在 Visual Studio 中打开 Web Project,并观察每一个文件的 Build Action 属性,你会发现几乎所有的文件都属于以下四种 Build Action

  • None:这意味着这个文件在构建过程中将不做任何处理。典型的例子是 Readme 或者 EULA(End User License Agreement) 文件。这种文件不会在打包中进行拷贝;
  • Compile:这类文件会在构建过程中进行编译,编译结果会嵌入到生成的程序集(dll 或者 exe)中。这类文件在打包的时候是不会进行拷贝的;
  • Content:这个文件不会在构建过程中进行编译。但是这个文件属于整个工程发布的一个部分。因此这类文件在打包的时候进行拷贝;
  • Embedded Resources:这个文件的内容将作为一种嵌入式资源在构建过程中嵌入到程序集中。这个文件在打包的过程中不会被拷贝;

因此,除了构建好的程序集之外,所有 Build ActionContent 的文件类型也会在打包的时候被拷贝。

以我们的工程为例:

FromZero.App
│ Global.asax [Content]
│ Global.asax.cs [Compile]
│ packages.config [Content]
│ Web.config [Content]
│ Web.Debug.config [None]
│ Web.Release.config [None]

├─bin
│ /* All build results are stored in this directory. */

├─Controllers
│ HomeController.cs [Compile]

├─Properties
│ AssemblyInfo.cs [Compile]

└─Views
└─Home
Index.cshtml [Content]

那么需要拷贝的文件为:

  • .\bin 文件夹下的所有文件;
  • 所有 Build ActionContent 属性的文件:Global.asax、packages.config、Web.config、Index.cshtml。

包的目录结构

在上一节我们介绍了,所有构建生成的程序集和 Build ActionContent 的文件都会在打包过程中进行拷贝。那么它们会拷贝到什么地方去呢?答案是拷贝到相应的目录下面去。以我们的工程为例,假设我们希望将构建好的工程拷贝到一个名为 Package 的目录下去,那么这个 Package 目录在打包完毕之后应该是这个样子的:

Package
│ Global.asax
│ packages.config
│ Web.config

├─bin
│ /* All build results. */

└─Views
└─Home
Index.cshtml

等一下,Controller 和 Properties 目录到哪里去了?由于这两个目录下面没有一个文件需要进行发布,因此这个目录也就不会创建。

假设你的确需要一个 Controller 目录进行发布,该怎么办呢?那么我们可以利用规则创建一个 0KB 的 placeholder 文件。并且将这个文件的 Build Action 属性设置为 Content

至此我们已经可以总结出打包的规则了:

  • 拷贝所有构建过程中生成的程序集文件,以及 Build ActionContent 的文件;
  • 将所有需要拷贝的文件拷贝到一个和其所在的工程目录对应的目录下面,如果某一个目录下没有一个文件需要在打包中进行拷贝,则不生成这个目录。

打包-代码

我们是否需要自己解析工程的 XML 结构然后按照上述规则进行打包呢?幸运的是,完全不用:这是因为在 ASP.NET Web 工程中会引用 $(VSToolsPath)\Web\Microsoft.Web.Publishing.targets,其中定义的 _WPPCopyWebApplication 过程正是我们以上描述的过程。我们只需要在上一个例子的基础上修改 Compile-Project 函数:

Function Compile-Project() {
iex -Command "& '$global_msBuildPath' /t:Rebuild /t:_WPPCopyWebApplication /p:WebProjectOutputDir='$global_buildDirPath\Package\' /p:UseWPP_CopyWebApplication=True /p:PipelineDependsOnBuild=False '$project_path'"
}

其中:

  • $global_msBuildPath 是 msbuild.exe 的所在位置;
  • /t:Rebuild:首先执行 Rebuild 过程,这将删除上一次的构建结果,然后重新构建整个项目;
  • /t:_WPPCopyWebApplication:将该项目进行打包;
  • /p:WebProjectOutputDir='$global_buildDirPath\Package\':将整个打包结果存放在 buildDir 下的 Package 目录下。如果这个目录不存在则创建这个目录;
  • /p:UseWPP_CopyWebApplication=True:从 Visual Studio 2010 开始,我们可以使用 Web.config.$(Configuration).config 文件对 Web.config 在不同的编译选项下进行修正。为了使用能够这个功能,需要设定此变量值为 True
  • /p:PipelineDependsOnBuild=False:如果将 UseWPP_CopyWebApplication 设置为 True,则必须将 PipelineDependsOnBuild 变量设置为 False 否则将导致 MSBuild 的 Targets 的循环引用。具体的技术细节请参见这里

这么长的一坨命令非常不容易维护,因此我们可以将这些命令放在一个 MSBuild 工程中。首先,我们建立一个 XML 文件,不妨命名为 Deploy.xml:

<?xml version="1.0" encoding="utf-8"?>
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="12.0">
<Target Name="Build">
<MSBuild
Projects="..\src\FromZero.App\FromZero.App.csproj"
Targets="Rebuild;_WPPCopyWebApplication"
Properties="WebProjectOutputDir=$(WebAppPublishDir);UseWPP_CopyWebApplication=True;PipelineDependsOnBuild=False;"/>
</Target>
</Project>

这样,我们只需要在 Compile-Project 函数中用 MSBuild 调用这个 Deploy.xml 文件,并将希望的包的输出目录赋值给 $(WebAppPublishDir) 变量即可:

$global_deployProject = "$global_buildDirPath\deploy.xml"

Function Compile-Project() {
iex -Command "& '$global_msBuildPath' /p:WebAppPublishDir='$global_buildDirPath\Package\' '$global_deployProject'"
}

到现在,Compile-Project 函数已经不止是在编译工程了,它还具备了打包的能力,因此我们将其重命名为 Deploy-Project

附:deploy.ps1 到目前为止的代码


$ErrorActionPreference = 'Stop' # Environment helpers ------------------------------------ Function Get-MsBuildPath() {
$msBuildRegPath = "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\12.0"
$msBuildPathRegItem = Get-ItemProperty $msBuildRegPath -Name "MSBuildToolsPath"
$msBuildPath = $msBuildPathRegItem.MsBuildToolsPath + "msbuild.exe"
return $msBuildPath
} # Environment variables ---------------------------------- $global_buildDirPath = Get-Location
$global_msBuildPath = Get-MsBuildPath
$global_solutionPath = "$global_buildDirPath\..\src"
$global_solutionFilePath = "$global_solutionPath\src.sln"
$global_nugetPath = "$global_buildDirPath\tools\nuget.exe"
$global_deployProject = "$global_buildDirPath\deploy.xml" # Install nuget packages --------------------------------- Function Install-SolutionPackages() {
iex "$global_nugetPath restore $global_solutionFilePath"
} $project_path = $global_solutionPath + '\FromZero.App\FromZero.App.csproj' Function Deploy-Project() {
iex -Command "& '$global_msBuildPath' /p:WebAppPublishDir='$global_buildDirPath\Package\' '$global_deployProject'"
} Install-SolutionPackages
Deploy-Project

ASP.NET MVC 从零开始 - 自动化部署(其二)的更多相关文章

  1. ASP.NET MVC 从零开始 - 自动化部署(其一)

    本文是从我的 github 博客 http://lxconan.github.io 导入的. 这是这个系列的第四篇了,前三篇请参见: ASP.NET MVC 从零开始 – Create and Run ...

  2. ASP.NET MVC 从零开始 - 请求处理

    这篇文章是从我的 github 博客 lxconan.github.io 导入的. 这是这个系列的第三篇了.前两篇文章请参见: ASP.NET MVC 从零开始 - Create and Run AS ...

  3. 总结一下ASP.NET MVC 网站的部署问题

    总结一下ASP.NET MVC 网站的部署问题 近日,准备把MVC建了一个新的测试站点部署到IIS上面,结果没想到出现了一系列的问题和错误,准备记录一下. 第一个问题,就是如何将MVC的站点部署到II ...

  4. ASP.NET MVC 使用MSBuild部署的几个注意事项

    ASP.NET MVC 使用MSBuild部署的几个注意事项 做新项目,当时参考NopCommerce的结构,后台Nop.Admin是一个独立的Area Web Site,但部署的时候发现,使用一键发 ...

  5. asp.net mvc 5发布部署遇到403.14

    asp.net mvc 5发布部署遇到403.14? HTTP错误 403.14 服务器配置为不列出此目录内容 除了设置.net运行的权限 isap和cgi启动状态外.可能是因为你手贱. 将这个钩去掉 ...

  6. ASP.NET MVC 从零开始 - Web.config

    这篇文章是从我的 github 博客 http://lxconan.github.io 导入的. 在上一篇中,我们从零开始创建了一个非常简单的 ASP.NET MVC 应用程序.接下来,你是不是期望我 ...

  7. 使用Visual Studio 2015 开发ASP.NET MVC 5 项目部署到Mono/Jexus

    最新的Mono 4.4已经支持运行asp.net mvc5项目,有的同学听了这句话就兴高采烈的拿起Visual Studio 2015创建了一个mvc 5的项目,然后部署到Mono上,浏览下发现一堆错 ...

  8. ASP.NET MVC 从零开始 - create and run

    这篇文章是从我的 github 博客 http://lxconan.github.io 导入的. 如果你想用 ASP.NET MVC 创建一个网络应用,那么你可以搜到很多的文章.但是没有多少文章告诉你 ...

  9. [ASP.NET MVC]EntityFramework离线部署

    根据项目需要可能会需要离线开发或者网速不好的情况下,很难配置EF,这种情况下就进行离线配置 (1)下载离线EF包: EF6.0的packages,百度网盘链接:https://pan.baidu.co ...

随机推荐

  1. Mac下golang开发环境配置

    go语言在开发效率和运行效率中的优势让很多人青睐,所以有倾向打算转向go语言的开发. 下面介绍在Mac OS X中golang的开发环境配置. 1.安装brew brew是一个mac下的由ruby开发 ...

  2. 前端项目通用、常用js common.js

    var url = location.href; if (url.toLowerCase().indexOf("/akweb_admin/") == -1) { function ...

  3. 【CentOS】LAMP相关3

    调优,安全如果是运维一个网站,PHP搭建的话,可能会出现500的错误,白页怎么去排查呢,今天就涉及到这方面的东西 http://blog.csdn.net/bsi_l4/article/details ...

  4. python学习4 常用内置模块

    logging os 路径处理 // 获取当前路径 os.path.abspath(__file__) //获取当前文件夹路径 os.path.dirname(os.path.abspath(__fi ...

  5. 关于Telerik RadGridView 数据列拖动后异常的一种情况

    目的: 想实现带有复杂表头(ColumnHeader)的列的动态加载,写了一个用户控件. 问题: 动态加载成功了,显示正常,滚动条也正常,但是一旦进行列的拖动操作之后,程序就挂掉了. 解决尝试: 反复 ...

  6. LintCode 77: 最长公共子序列

    public class Solution { /** * @param A, B: Two string. * @return: the length of the longest common s ...

  7. MAC帧和IP包的分析

    ping了12次岭南师范学院官网后退出   抓到的包如下   各个名词解释

  8. dd——留言板再加验证码功能

    1.找到后台-核心-频道模型-自定义表单 2.然后点击增加新的自定义表单 diyid 这个,不管他,默认就好 自定义表单名称 这个的话,比如你要加个留言板还是投诉建议?写上去呗 数据表  这个不要碰, ...

  9. 获得Map的选择集

    ISelection selection = m_hookHelper.FocusMap.FeatureSelection;            IEnumFeatureSetup iEnumFea ...

  10. 输入/输出系统的四种不同工作方式对CPU利用率比较

    程序控制工作方式:输入/输出完全由CPU控制,整个I/O过程中CPU必须等待其完成,因此对CPU的能力限制很大,利用率较低 程序中断工作方式:CPU不再定期查询I/O系统状态,而是当需要I/O处理时再 ...