\n\ndotnet cli是 .Net Core功能中最有用的特性之一。在这篇文章里,我们将介绍几个.Net OSS工具是如何使用dotnet cli,并介绍如何在日常开发中使用新的cli工具。
\n
正文
\n关键要点
\n- \n
- dotnet cli使得基于. Net项目的自动化和脚本编写变得非常简单,尤其是与十多年前的. Net技术相比。\n
- dotnet cli可扩展性模型创造了条件,使得通过Nuget将外部.NET编写的命令行程序集成到你的自动化构建中成为可能。\n
- dotnet cli允许在你的构建脚本中针对解决方案进行测试。\n
- dotnet cli的测试输出有助于更好地使用持续集成(CI)。\n
- 使用Docker之类的容器技术比使用dotnet cli要容易得多。\n
随着.NET Core 2.0的发布,微软拥有了通用、模块化、跨平台和开源平台的下一个主要版本,该版本最初于2016年发布。.NET Core已经创建了许多API,这些API在.NET框架的当前版本中是可用的。它最初是为了下一代ASP.NET解决方案创建的,但现在是许多其他场景的驱动和基础,包括物联网、云和下一代移动解决方案。在关于.NET Core的第二个系列的文章中,我们将进一步探讨.NET Core的优点,以及它如何不仅有益于传统的.NET开发人员,也有益于所有需要为市场提供强健的、高效的和经济的解决方案的技术人员。
\nInfoQ的这篇文章是“.NET Core\u0026quot;系列的一部分。您可以通过RSS订阅接收通知。
\n最近总有人问我,和那些要么迟疑,要么不能退出旧版本、全功能的.NET的人相比,选择.NET Core的优势是什么?我在回答中会提到.NET Core 有更好的性能、改进的csproj文件格式、改进的ASP可测试性,并且它是跨平台的。
\n作为几个OSS工具(Marten、StructureMap,以及在这个项目中作为例子被引用的Alba)的作者,对我个人而言最大的优势可能是dotnet cli的出现。我个人认为,结合新的.NET SDK csproj文件格式一起使用时,dotnet cli工具使我可以更容易创建项目和维护构建脚本。我可以更容易在构建脚本中运行测试,更容易使用和分发Nuget包,cli可扩展性机制非常适合将通过Nuget包分发的自定义可执行文件合并到自动构建中。
\n若要开始使用dotnet cli,首先要在开发机器上安装.NET SDK。安装完成后,给你一些有用的提示:
\n- \n
- 将“dotnet”工具全局安装到你的PATH中,这样在任何地方都可以通过命令行提示符使用它。\n
- dotnet cli采用Linux风格的命令语法,用“–word [value]”这种普通写法表示选择的参数,或者直接用缩写形式“-w [value]”。如果您习惯Git或Node.js命令行工具,就不会对dotnet cli感到陌生。\n
- “dotnet --help”将列出已安装的命令和一些基本语法用法。\n
- “dotnet --info”将告诉你使用的是哪个版本的dotnet cli。在持续集成构建中调用此命令可能是一个好主意,以便在本地工作并在构建服务器失败时排除故障,反之亦然。\n
- 尽管我在本文中讨论的是.NET Core,但是请注意,你可以在完整.NET框架的以前版本中使用新的SDK项目格式和dotnet cli。\n
命令行中的Hello World
\n为了简单了解一下dotnet cli的一些亮点,让我们假设想构建一个简单的“Hello World”ASP.NET Core应用程序。不过,为了好玩,我们来添加一些新花样:
\n1.我们的web服务将在一个单独的项目中进行自动化测试。
\n2.我们将通过Docker容器部署我们的服务,因为这是很酷的做法(它展示了更多的dotnet cli)。
\n3.当然,我们将尽可能多地使用dotnet cli。
\n如果您想看到这段代码的最终结果,请查看this GitHub repository。
首先,让我们从一个名为“DotNetCliArticle”的空目录开始,并打开您最喜欢的命令行工具到该目录。我们将从使用“dotnet new”命令来生成解决方案文件和新项目开始。.NET SDK附带了几个用于创建常见项目类型或文件的通用模板,以及其他可作为外接程序使用的模板(稍后部分将对此进行详细介绍)。要查看在你的机器上可用的模板,可以使用以下命令dotnet new -help,它应该会给出如下输出:
\n你可能会留意到有一个sln模板,它针对的是空解决方案文件。我们将使用该模板,键入dotnet new sln命令,该命令将生成以下输出:
\nThe template \u0026quot;Solution File\u0026quot; was created successfully.\n
\n
默认情况下,此命令将以包含的目录命名解决方案文件。因为我将根目录命名为为“DotNetCliArticle”,所以生成的解决方案文件是“DotNetCliArticle.sln”。
\n接下来,让我们用以下命令添加“Hello,World”的实际项目:
\ndotnet new webapi --output HeyWorld\n
\n
上面的命令意思是将“webapi”模板用到通过“output”参数选择的“HeyWorld”中。这个模板将生成一个精简的MVC Core项目结构,适合于无头API。同样,默认的做法是根据所在的目录命名项目文件,因此我们在目录下得到一个名为“HeyWorld.csproj”的文件,以及所有基本文件,组成一个最小的ASP.NET MVC Core API项目。该模板还设置了所有必要的Nuget对ASP.NET Core的引用,我们在新项目启动时会用到它们。
\n由于我刚好在一个小型Git存储库中构建了它,在使用Git add添加了任何新文件之后,我使用Git status查看新创建的文件:
\n new file: HeyWorld/Controllers/ValuesController.cs\n new file: HeyWorld/HeyWorld.csproj\n new file: HeyWorld/Program.cs\n new file: HeyWorld/Startup.cs\n new file: HeyWorld/appsettings.Development.json\n new file: HeyWorld/appsettings.json\n
\n
现在,要将新项目添加到我们的空解决方案文件中,您可以像这样使用“dotnet sln”命令:
\ndotnet sln DotNetCliArticle.sln add HeyWorld/HeyWorld.csproj\n
\n
现在我们有了一个新的ASP.NET Core API服务作为外壳,无需打开Visual Studio.NET(或者是JetBrains Rider)。为了更进一步,在编写任何实际代码之前启动我们的测试项目,我发出以下命令:
\ndotnet new xunit --output HeyWorld.Tests\ndotnet sln DotNetCliArticle.sln add HeyWorld.Tests/HeyWorld.Tests.csproj\n
\n
上面的命令使用xUnit.NET创建一个新项目,并将该新项目添加到我们的解决方案文件中。测试工程需要对“HeyWorld”的工程引用,幸运的是,我们可以使用很棒的“dotnet add”工具添加工程引用,如下所示:
\ndotnet add HeyWorld.Tests/HeyWorld.Tests.csproj reference HeyWorld/HeyWorld.csproj\n
\n
在打开解决方案之前,我知道还有一些Nuget参考资料,我想在测试项目中使用它们。我选择的断言工具是Shoully,因此我将通过对命令行发出另一个调用来添加对最新版本的shoully的引用:
\ndotnet add HeyWorld.Tests/HeyWorld.Tests.csproj package Shouldly\n
\n
命令行的输出如下:
\ninfo : Adding PackageReference for package 'Shouldly' into project 'HeyWorld.Tests/HeyWorld.Tests.csproj'.\nlog : Restoring packages for /Users/jeremydmiller/code/DotNetCliArticle/HeyWorld.Tests/HeyWorld.Tests.csproj...\ninfo : GET https://api.nuget.org/v3-flatcontainer/shouldly/index.json\ninfo : OK https://api.nuget.org/v3-flatcontainer/shouldly/index.json 109ms\ninfo : Package 'Shouldly' is compatible with all the specified frameworks in project 'HeyWorld.Tests/HeyWorld.Tests.csproj'.\ninfo : PackageReference for package 'Shouldly' version '3.0.0' added to file '/Users/jeremydmiller/code/DotNetCliArticle/HeyWorld.Tests/HeyWorld.Tests.csproj'.\n
\n
接下来,我想向名为Alba的测试项目添加至少一个Nuget引用。我将使用AspNetCore2来编写针对新的web应用程序的HTTP契约测试:
\ndotnet add HeyWorld.Tests/HeyWorld.Tests.csproj package Alba.AspNetCore2\n
\n
现在,在使用代码之前先检查一下,我将在命令行发出以下命令构建解决方案中的所有项目,确保它们都可以正常编译:
\ndotnet build DotNetCliArticle.sln\n
\n
由于Alba.AspNetCore2和ASP.NET Core Nuget在HeyWorld项目中的引用之间的菱形依赖版本的冲突,所以没有编译。不过不用担心,因为这个问题很容易解决,只需修复Microsoft.AspNetCore的版本依赖关系即可。测试项目中的所有Nuget都是这样的:
\ndotnet add HeyWorld.Tests/HeyWorld.Tests.csproj package Microsoft.AspNetCore.All --version 2.1.2\n
\n
在上面的示例中,使用值为“2.1.2”的“–version”标志将修复对该版本的引用,而不仅仅是使用从Nuget提要中找到的最新版本。
\n为了再次检查我们的Nuget依赖问题是否已经解决,我们可以使用下面的命令进行检查,它比重新编译所有东西要更快:
dotnet clean \u0026amp;\u0026amp; dotnet restore DotNetCliArticle.sln\n
\n
作为一个有经验的.NET开发人员,我非常担心临时/obj和/bin文件夹中残留的文件。因此,我在Visual Studio中使用“Clean Solution”命令,以防我试图改变引用的时候落下些什么。从命令行执行“dotnet clean”命令是完全相同的操作。
\n同样,针对众所周知的Nuget依赖问题,“dotnet restore”命令在解决方案中都试着去解决了。在这种情况下,使用“dotnet restore”可以让我们快速发现任何潜在的冲突或丢失的Nuget引用,而无需进行完整的编译,我在自己的工作中主要就采用该命令。在最新版本的dotnet cli中,在调用“dotnet build/test/pack/etc”时,会自动为您完成Nuget解析(该行为可以用标记覆盖),这将首先需要Nuget。
\n我们调用的“dotnet restore DotNetCliArticle.sln”干净利落地运行完毕,没有错误,所以我们终于可以准备编写一些代码了。让我们打开您选择的C#编辑器,向HeyWorld添加一个代码文件。测试项目包含一个非常简单的HTTP协议测试,它将指定我们希望从新的HeyWorld应用程序中的“GET: /”路由获得的行为:
\nusing System.Threading.Tasks;\nusing Alba;\nusing Xunit;\nnamespace HeyWorld.Tests\n{\n public class verify_the_endpoint\n {\n [Fact]\n public async Task check_it_out()\n {\n using (var system = SystemUnderTest.ForStartup\u0026lt;Startup\u0026gt;())\n {\n await system.Scenario(s =\u0026gt;\n {\n s.Get.Url(\u0026quot;/\u0026quot;);\n s.ContentShouldBe(\u0026quot;Hey, world.\u0026quot;);\n s.ContentTypeShouldBe(\u0026quot;text/plain; charset=utf-8\u0026quot;);\n });\n }\n }\n }\n}\n
\n
结果文件应该保存在具有适当名称(如verify_the_endpoints.cs)的HeyWorld.Tests目录。
\n在没有深入了解Alba机制前,我们的新HeyWorld应用的首页路由应该写出“Hey, world”。虽然我们还没有在HeyWorld应用中编写任何实际的代码,但是我们仍然可以运行这个测试,看看它能够连接正确,还是因为某些“理所当然的理由”而失败。
\n回到命令行,我可以使用以下命令运行测试项目中的所有测试:
\ndotnet test HeyWorld.Tests/HeyWorld.Tests.csproj\n
\n
我们的一个测试会失败,因为还没有实现任何东西,它给了我们这样的输出:
\nBuild started, please wait...\nBuild completed.\nTest run for /Users/jeremydmiller/code/DotNetCliArticle/HeyWorld.Tests/bin/Debug/netcoreapp2.1/HeyWorld.Tests.dll(.NETCoreApp,Version=v2.1)\nMicrosoft (R) Test Execution Command Line Tool Version 15.7.0\nCopyright (c) Microsoft Corporation. All rights reserved.\nStarting test execution, please wait...\nTotal tests: 1. Passed: 1. Failed: 0. Skipped: 0.\nTest Run Successful.\nTest execution time: 2.4565 Seconds\n
\n
为了把输出求和,执行了一个测试,但是失败了。我们还可以看到标准的xUnit输出,它提供了一些关于测试失败原因的信息。这里需要注意的是,“dotnet test”命令将返回一个退出代码,如果所有测试都通过,则返回0,表示成功;如果任何测试失败,则返回一个非零退出代码,表示失败。这对于持续集成(CI)脚本非常重要,大多数CI工具使用任何命令的退出代码来确定构建何时失败。
\n我认为上面的测试之所以失败是因为“理所当然的原因”,这意味着测试工具似乎能够引导真正的应用程序,我希望得到404响应,因为还没有编写任何代码。接下来,让我们为预期的行为实现一个MVC Core 端点:
\npublic class HomeController : Controller\n{\n [HttpGet(\u0026quot;/\u0026quot;)]\n public string SayHey()\n {\n return \u0026quot;Hey, world!\u0026quot;;\n }\n}\n
\n
(注意,前面的代码应该作为HeyWorld\\startup.cs文件中的附加类添加)
\n再次回到命令行,让我们运行前面的“dotnet test HeyWorld.Tests/HeyWorld.Tests.csproj”命令,希望看到这样的结果:
\nBuild started, please wait...\nBuild completed.\nTest run for /Users/jeremydmiller/code/DotNetCliArticle/HeyWorld.Tests/bin/Debug/netcoreapp2.1/HeyWorld.Tests.dll(.NETCoreApp,Version=v2.1)\nMicrosoft (R) Test Execution Command Line Tool Version 15.7.0\nCopyright (c) Microsoft Corporation. All rights reserved.\nStarting test execution, please wait...\nTotal tests: 1. Passed: 1. Failed: 0. Skipped: 0.\nTest Run Successful.\nTest execution time: 2.4565 Seconds\n
\n
好了,现在测试通过了,让我们运行实际的应用程序。由于“dotnet new webapi”模板使用进程内的 in-process Kestrel web server 来处理HTTP请求,所以要运行新的HeyWorld应用程序,我们唯一需要做的一件事就是从命令行使用以下命令启动它:
\n dotnet run --project HeyWorld/HeyWorld.csproj\n
\n
运行上面的命令应该会得到如下输出:
\nUsing launch settings from HeyWorld/Properties/launchSettings.json...\n: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]\nUser profile is available. Using '/Users/jeremydmiller/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.\nHosting environment: Development\nContent root path: /Users/jeremydmiller/code/DotNetCliArticle/HeyWorld\nNow listening on: https://localhost:5001\nNow listening on: http://localhost:5000\nApplication started. Press Ctrl+C to shut down.\n
\n
要测试我们现在正在运行的新应用程序,只需在浏览器中导航如下:
\n处理HTTPS设置超出了本文的范围。
\n请再次注意,我假设所有命令都是在将当前目录设置为解决方案根文件夹的情况下执行的。如果当前目录是一个项目目录,并且只有一个*.csproj。那么,您只需在该目录下键入“dotnet run”即可。现在我们已经有了一个经过测试的web api应用程序,接下来让我们将HeyWorld放到Docker镜像中。使用 the standard template for dockerizing a .NET Core application,我们将向HeyWorld项目添加一个Dockerfile,内容如下:
\nFROM microsoft/dotnet:sdk AS build-env\nWORKDIR /app\nCopy csproj and restore as distinct layers\nCOPY *.csproj ./\nRUN dotnet restore\nCopy everything else and build\nCOPY . ./\nRUN dotnet publish -c Release -o out\nBuild runtime image\nFROM microsoft/dotnet:aspnetcore-runtime\nWORKDIR /app\nCOPY --from=build-env /app/out .\nENTRYPOINT [\u0026quot;dotnet\u0026quot;, \u0026quot;HeyWorld.dll\u0026quot;]\n
\n
(注意,前面的文本应该保存到项目目录中名为Dockerfile的文本文件中——在本例中是HeyWorld\\Dockerfile)。
\n因为这篇文章仅仅是关于dotnet cli的,我只想关注Dockerfile中它的两种用法:
\n1.“dotnet restore”–正如我们在上面学到的,这个命令将解决应用程序的任何Nuget依赖关系。
\n2.“dotnet publish -c Release -o out”–“dotnet publish”命令将构建指定的项目,并将组成应用程序的所有文件复制到给定位置。在我们的例子中,“dotnet publish”将为HeyWorld本身复制已编译的程序集、从Nuget依赖项引用的所有程序集、配置文件以及csproj文件中引用的任何文件。
\n请注意,在上面的用法中,我们必须通过使用“-c Release”标志明确地告知“dotnet publish”用“Release”配置编译。那些用于编码的dotnet cli命令(例如“build”、“publish”、“pack”)如果没有指定,将以 “Debug”为默认值。注意这种行为,如果要发布用于生产的Nuget或应用程序,请记住指定“-c Release”或“-configuration Release”。别怪我没提醒你。
\n为了完成整个周期,我们现在可以使用以下命令通过Docker构建和部署我们的小HeyWorld应用程序:
\ndocker build -t heyworld .\ndocker run -d -p 8080:80 --name myapp heyworld\n
\n
第一个命令为我们的应用程序“heyworld”构建并本地发布Docker镜像。第二个命令实际上作为一个名为“myapp”的Docker容器运行我们的应用程序。您可以打开浏览器访问“http://localhost:8080”予以验证。
\n总结
\ndotnet cli使得基于. NET项目的自动化和脚本编写变得非常简单,尤其是与十多年前的.NET技术相比。在许多情况下,您甚至可能会避开任何基于任务的构建脚本工具(Cake、Fake、Rake、Psake等),而选择只委托给dotnet cli的简单shell脚本。此外,dotnet cli可扩展性模型可以很容易地将外部.NET授权的命令行应用程序通过Nuget分布到自动构建程序中。
\n关于作者
\n杰里米·米勒(Jeremy Miller)在密苏里州一个农场社区长大,那里有一群“特别”的人,名叫“树荫技工”。通常,他们不是世界上最有名望的人,但他们有解决机械问题的诀窍,而且做事鲁莽无畏。如果你发现从一辆停在街区的通勤车下伸出来两条腿,那他想必就是名修理工了,他的周围是一些骨架车,堆挤在他那长满灌木、堆满垃圾的院子里。你看到的被遗弃在他周围的打浆机并不是没用的,他们是素材。他会零零碎碎地进行些小调整,然后根据你的需要想出一个创造性的解决方案。尽管名声一般,但一个树荫技工知道如何让东西运行。虽然米勒没有任何特殊的机械能力(尽管他拥有机械工程学位),但他喜欢把自己当成一个像树荫技工似的开发人员。他的硬盘上肯定到处都是废弃的开源项目碎片。
\n随着.NET Core 2.0的发布,微软拥有了通用、模块化、跨平台和开源平台的下一个主要版本,该版本最初于2016年发布。.NET Core已经创建了许多API,这些API在.NET框架的当前版本中是可用的。它最初是为了下一代ASP.NET解决方案创建的,但现在是许多其他场景的驱动和基础,包括物联网、云和下一代移动解决方案。在关于.NET Core的第二个系列的文章中,我们将进一步探讨.NET Core的优点,以及它如何不仅有益于传统的.NET开发人员,也有益于所有需要为市场提供强健的、高效的和经济的解决方案的技术人员。
\nInfoQ的这篇文章是“.NET Core\u0026quot;系列的一部分。您可以通过RSS订阅接收通知。
\n查看英文原文:A Quick Tour of the .NET CLI