这篇文章是从我的 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. 通过FileHandle获取FileObject对象

    <div id="wrap"> <!-- google_ad_section_start --> NTSTATUS MyNtReadFile(<br& ...

  2. SQL SERVER 得到汉字首字母函数四版全集 --【叶子】

    --创建取汉字首字母函数(第三版) create function [dbo].[f_getpy_V3] ( ) ) ) as begin ),) ,@len = len(@col),@sql = ' ...

  3. 进击的Python【第十章】:Python的socket高级应用(多进程,协程与异步)

    Python的socket高级应用(多进程,协程与异步)

  4. 安装openssl 扩展的时候出现Cannot find config.m4. Make sure that you run '/usr/local/php/bin/phpize' in the top level source directory of the module的解决方法

    进入php源码包目录:cd /usr/local/php-5.6.25/ext/openssl 执行命令:  cp ./config0.m4 ./config.m4 即可

  5. 在Spring中轻松写日志

    最近觉得写的一点代码(JAVA),还觉得颇为自得,贡献出来供大家参考. 首先,先上代码: @Controller public class Controller1{ @WriteLog(value = ...

  6. appium如何获取conten-desc内容文本

    如何获取conten-desc内容文本 定位到该元素,通过getAttribute("name");来获取内容如:媒体报道 总结: 思路和selenium一样,可以理解为获取它的v ...

  7. Python中的生成器与yield

    对于python中的yield有些疑惑,然后在StackOverflow上看到了一篇回答,所以搬运过来了,英文好的直接看原文吧. 可迭代对象 当你创建一个列表的时候,你可以一个接一个地读取其中的项.一 ...

  8. PD PDM模型中关系设置为概念模型样式

      来自为知笔记(Wiz)

  9. iOS Swift 3 open

    参考资料:http://stackoverflow.com/questions/38947101/what-is-the-open-keyword-in-swift

  10. EasyUi

    <base href="<%=basePath %>"> -- (不推荐使用)--导入文件路径 ${pageContent.request.contextP ...