这篇文章是从我的 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. linux简单命令

    查看服务器开启的进程信息[root@CentOSHT ~]# top 其中第一行的  Load average 参数是服务器负载的意思,

  2. WooCommerce插件设置教程之设置主页

    http://demo.themes4wp.com/documentation/homepage-setup/#videoimage-tutorial

  3. SQL函数说明大全

    一旦成功地从表中检索出数据,就需要进一步操纵这些数据,以获得有用或有意义的结果.这些要求包括:执行计算与数学运算.转换数据.解析数值.组合值和聚合一个范围内的值等. 下表给出了T-SQL函数的类别和描 ...

  4. Cookie 用法 小记

    //保存cookie Cookie cookieName = new Cookie("name", realUser.getName());                 Coo ...

  5. Android中轻松显示Gif图片

    android中现在没有直接显示gif的view,只能通过mediaplay来显示,且还常常不能正常显示出来,为此写了这个gifview,其用法和imageview一样使用方法:1-把GifView. ...

  6. mssql与mysql 数据迁移

    概要: mssql向mysql迁移的实例,所要用到的工具bcp和load data local infile. 由于订单记录的数据是存放在mssql服务器上的,而项目需求把数据迁移到mysql ser ...

  7. HDU 4801 Pocket Cube

    题目链接 去年现场,虎哥1Y的,现在刷刷题,找找状态... 一共6种转法,把3个面放到顶部,左旋和右旋,感觉写的还不错....都写成常数了. #include <stdio.h> #inc ...

  8. sql查询重复数据

    select *from Awhere id in (select id from A group by id having count(1) >= 2) 注释:id 为重复的关键字(更换成所需 ...

  9. 【翻译】Express web应用开发 第一章

    本章节是一个对初学者友好的Express介绍.你将学习到Express的基础知识.核心概念和实现一个Express应用的组成部分.现阶段我们不需要做太多的编码,本章节会让你熟悉和习惯Express,为 ...

  10. can't connect to mysql server on 'localhost'(10061)

    在linux下安装Navicat,想说在windows下试一试phpmyadmin之外的mysql图形工具. 显示下载安装了mysql workbench,链接成功.然后又弄了一下输入法重启,想说试一 ...