如何引导和创建.NET项目


/**
 * 原文出处:https://www.toptal.com/dot-net/bootstrap-and-create-dot-net-projects
 * @author dogstar.huang <chanzonghuang@gmail.com> 2016-03-22
 */

创建.NET项目

使用Visual Studio向导创建一个.NET项目就像嗖一下那么简单。去到文件 => 新项目,或者``添加新项目```到一个已有的解决方案。 一旦新项目创建好后,你就可以马上进行编码了。然而,由向导产生的默认项目配置对于专业的团队是很难接受的,因为他们设定的质量太 低了。况且,也没有向导可以知道你需要在特定开发环境所执行的其他启动步骤。

在此篇文章中,我会带你走进若干个重要的、当你一旦创建项目就应该开启的配置,这对于最小化未来的技术债务很重要。同时,我们还会 回顾很多.NET开发人员在他们构建解决方案和新项目时会应用的一些通用实践。即使你没有应用其中 的某些想法,学习和大致了解一下大部分团队所做的也是很不错的。

结构

拥有一个良好定义的结构对于复杂项目来说是至关重要的。这提高了新人加入一个团队时的入职体验,也使得你在支持遗留项目时生活更轻 松。对于良好的结构,这有两个关键的指标:
+ 使用解决方案和项目目录 + 一致的命名

目录

解决方案目录,有时简称为 虚拟目录 ,是一个对项目进行分组的非常方便的 工具。在解决方案资源管理器 视图简单地右键并且选择添加 => 新的解决方案目录,然后拖放任何已存在的项目到这个新目录。这 些目录不会映射到文件系统,以便让你保持物理结构的不变性,所以把这个项目从一个解决方案目录移到另一个不会发生物理上的移动。

带数字前缀不是必要的,但可以使得这些目录在 解决方案目录 窗口中以正确的顺序排列显示。

通过利用分割单解或多解模型,Visual Studio可以同时把多个解决方案一 起工作。他们用之甚少,所以在此篇文章中我不会涵盖这块。

不像 解决方案目录项目目录 匹配着物理目录结构所以如同真实的目录一样会在硬盘上持久化。此外,包括了C#代码的项目目录应该 匹配上项目的 命名空间 。这使得导航相当自然。你甚至可以开启ReSharper规则来提醒错配。

命名

这里有一些值得推荐的命名规则:
+ 使用驼峰拼写法 + 项目名称应该与其输出程序集名称匹配 + 包含自动化测试的项目应该有后缀.Tests + 全部项目名称都应有一个公共的前缀,如Company.Product

这里也有一些合理的规则。当要应用他们时你应该根据常识(和英语语法,当然)自行决定是否采用。
+ 当一个容器(项目或者目录)包含多种同类(例如Tests或者System.Collections)实例时使用名词复数形式 + 当整个容器包含的代码都只关于某个单一实体时(例如System.Collections.ObjectModel)使用单数形式 + 对于短缩写,像System.IO这样使用大写 + 对于长缩写,像Modules.Forex.这样使用驼峰法

经验法则:短缩写不应该超过三个字符。

配置解决方案

配置一个解决方案就如为你的环境提供全部需要的基础文件一样简单。尽管有些可以稍候再添加(一如CI集成文件),但是某些文件你最好在 一开始就早早引入。

ReSharper设置

如果你是专业的.NET开发人员,那么你很可能会使用ReSharper。ReSharper能够非常灵活地管理配置。作为一个team leader,你可以创建和 分发会被其他开发人员使用到的 团队共享 配置。 团队共享 配置存放在一个以.DotSettings作为扩展名的文件里。若这个文件 名字与Visual Studio解决方案的名称匹配时ReSharper将会自动加载这些配置:

MyCompany.MyProduct.sln  
MyCompany.MyProduct.sln.DotSettings  

所以,如果最终想把某些配置应用到整个团队,你应该一开始就创建这个文件。一个好的示例可以是使用(或者不使用)var关键字这条 规则。你的 团队共享 配置可以只有这么一条规则,而其他则是开发人员自己的偏好设置。值得提醒的是可以在每个项目的级别上设置同样 的ReSharper配置,因为你可能会有相同的但不能修改的逻辑代码(例如换成使用var关键字)。

如果你正确命名了这个文件,如上面示例所示,那么任何带有快速ReSharper启动的Visual Studio的实例都会自动加载这个文件并且强制实施 这些规则。别忘记了把这个文件提交到源代码版本控制。

StyleCop规则

和ReSharper设置一样,StyleCop设置也可以共享。如果使用了ReSharper,那么很可能已经安装了从ReSharper利用StyleCop的集成插件。然而, StyleCop把它的设置独立保存到了Settings.StyleCop文件。相似地,可以把这个文件和解决方案以及项目文件放在一起。

如果你正在使用StyleCop,别忘了运行StyleCop配置工具以及关掉不想执行的检查。默认情况下,全部的检查都是开启的。保存新的设置到这个 文件并提交到源代码版本控制。

文本文件

如果正在构建一个公开的项目并且打算发布源代码,别忘了也创建和提交这些文件:

README.md  
LICENSE  

对于README.md文件我推荐使用markdown格式,因为它成为了个体标准并且被像GitHub这样的公共源码控制服务以及像BitBucket(前身是 Stash)这样的内部服务所支持。

NuGet规格

如果正在构建一个准备贡献给NuGet Gallery的类库,那么很可能需要创建包规格文件,如MyProject.nuspec。我更倾向于手动创建这 些文件并提交到源代码版本控制。包通常会由某个持续集成(缩写为:CI)任务来发布,也可以在任何时候从控制台构建和发布一个包,如:

nuget.exe pack MyLibrary.nuspec  

千万别忘了在执行这条命令前增加包的版本号。

CI规格文件

我们使用的CI服务不尽相同,而且配置的脚本和设置也多种多样。在这里我只会提及一些你可能会考虑添加的公共插件:
+ NUnit设置,指定针对特定的任务哪些测试集合会在CI服务器上执行。全部的测试几乎都分成几类。单元测试 应该在每一次构建时都执行,性能测试 在夜里执行,而集成测试 在每次发布基准时执行。 + NCover设置,指定针对测试覆盖率会分析哪些测试集合。 + SonarQube设置,决定收集哪些软件度量指标。 + 任务脚本,如NAnt,PowerShell或者Windows批处理文件。

正确地引导项目可以减少未来的技术债务并且能让源代码具有可读性以及看起来更专业。

配置项目

被命名为.csproj或者.vbpro的项目文件,包含了全部被Visual Studio和MSBuild使用的配置。然而,并不是全部的配置都 可以从项目属性窗口获得。为了手动编辑在Visual Studio中的这些文件,你需要:
+ 在解决方案资源管理器右键项目 + 选择 卸载项目 + 再次右键并选择 编辑xyz.csproj + 完成编辑 + 在项目上再次右键,选择 重新加载项目

另外,可以用你喜欢的文本编辑器打开项目文件,编辑并保存。当你切换回到Visual Studio窗口时,你将会看到重新加载已修改的项目 的弹窗提醒。

提醒控制

构建一个高质量的软件绝对不忽略构建警告。所以,应该开启最大数量的警告并且将警告作为错误来对待。注意到对于所有的构建配置 都应该这么做,一如Debug和Release。最好的方式是把以下配置写到公共属性组里:

<WarningLevel>4</WarningLevel>  
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>  

并且确保在其他属性组里没有相同的配置。否则,他们会覆盖公共组里相应的属性。

FxCop

运行FxCop只是每次构建时要做的实践。大部分团队更喜欢不时地(通常在发布前)运行FxCop以便确保不会引入服务错误。然而,如果 你想在每次构建最终执行检查,添加这个选项:

<RunCodeAnalysis>true</RunCodeAnalysis>  

注意FxCop,和StyleCop一样,有自己的设置并且可以放置到根目录以及添加到源代码版本控制。当在CI服务器是运行FxCop时很可能会 用到这些设置。

文档

这部分是关于XmlDoc的。如果正在构建的是公共的API,那么应该创建和维护API文档。大部分开发人员以API开发开始(实际是写代码), 并且仅在发布前开启项目设置Build / XML documentation file。很自然地,在另一个重新构建后会出现一堆错误,因为每一个缺 失的XmlDoc都会导致一个构建错误。为了避免这一点,你应该早早地开启上述提到的选项。

代码契约

代码契约是来自微软研究院的一个很棒的框架,它可以允许你在代码 中为运行时检测、静态分析和文档表达前置条件、后置条件和对象不变量。在很多关键的项目中我都使用了代码契约,很是有用,所以 我鼓励你也试一下。

如果决定使用代码契约,那么在一开始就开启契约是很重要的,也就是当你刚刚创建一个新项目时。在开发的中途添加契约也是可以的, 但需要对很多类做出改变,以便契约相互匹配。所以,别忘了开启全部必要的设置(至少CodeContractsEnableRuntimeChecking) 并且确保这些设置出现在公共属性组里。

StyleCop稽查

在这之前我们讨论了针对开发时期的StyleCop配置。然而,当你的项目构建在CI服务器上时,ReSharper就没用了并且我们应该开启StyleCop 验证来和MSBuild一起执行。

这通常通过手动编辑项目文件来完成。你需要在Visual Studio卸载项目,编辑项目文件然后把项目加载回来:

<PropertyGroup>  
   <!— add this to the common property group (common to Debug/Release/etc) —>
   <StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
</PropertyGroup>

<!— add this Import in the very bottom —>  
<Import Project="$(ProgramFiles)\MSBuild\Microsoft\StyleCop\v4.3\Microsoft.StyleCop.targets">  

StyleCopTreatErrorsAsWarnings 配置会按它说的那样来做:它会中断构建任何StyleCop规则验证。导入标签的元素对于MSBuild 添加StyleCop任务到构建链是必要的。

你可能注意到了指向Program Files的路径。因为开发人员已经安装了不同版本的StyleCop,一些团队更喜欢在源代码版本控制保 持一个同样的StyleCop安装的私有备份,路径可以是相对的。这也使得CI服务器启动更容易,因为不需要在本地安装StyleCop。

程序集信息

每一个由Visual Studio向导创建的.NET项目都会有自动填充了一些Assembly属性的AssemblyInfo.cs文件(请看 Properties 子目录), 但没有向导可以为你填充全部Assembly属性。确保至少这些属性已被填充:
+ AssemblyTitle + AssemblyDescription + AssemblyCompany + AssemblyProduct + AssemblyCopyright + AssemblyVersion

对于任何准备投身其中的集合来说这些都是最低限度的要求。在这背后一个实用的原因是NuGet:如果正在使用来自选择程序集文件的 自动化NuGet规格创建,这个工具会从这些属性中导出需要的信息。

你也可以在一开始就填充一个额外的属性:

InternalsVisibleTo  

这个属性使得内部类和接口对于指定程序集是可见的。这通常用于为项目准备创建的自动化测试。

连接字符串

如果管理连接字符串在Stack Overflow上是一个非常流行的问题。问题 就是如何使得连接字符串对于每一个开发人员或者每个CI任务是唯一的,并且当发布源代码时不会暴露连接的详情信息。

App.config(针对桌面应用)或者Web.config(针对网页应用),进行以下配置以便在运行时加载user.config文 件。把这个放到你的源代码版本控制下:

<?xml version="1.0" encoding="utf-8" ?>  
<configuration>  
    <connectionStrings configSource="user.config"></connectionStrings>
</configuration>  

显而易见地,user.config文件应该在源代码版本控制中排除掉,并且每一个开发人员应该在本地有一份副本,保护连接字符串的隐私:

<connectionStrings>  
    <add name="test" connectionString="Server=.;Database=...;"/>
</connectionStrings>  

.gitignore

对于那些使用Git作为源代码版本控制的人,把一些文件的正则添加到.gitignore文件是很重要的。然而,我们机智的社区已构建 了一个”放之四海而皆准“的文件,可以在这里找到:
github.com/github/gitignore/blob/master/VisualStudio.gitignore

你应该把这个作为一个参考的.gitignore文件并且添加额外需要的自定义排除规则。

GitHub徽章

你可能看过了在项目主页上很高大上的README。如果你正在发布项目到GitHub上,考虑把你的项目和公共服务连接起来以便:
+ 构建:展示一个构建是成功还是失败 + 测试:展示测试覆盖率和测试执行状态 + 发布:展示最新的NuGet包版本

一个完整的徽章列表以及相关的服务可以在shields.io找到。你也许找到了很多有趣的开源项目徽章。

一旦你通过某个选定的服务注册了项目,你会给定一个指向图片的链接以及和一个弄好的markdown语法链接,而这些你可以添加到你 的README.md文件。顺便说一下,这也是其中一个原因为什么你应该为 _Readme _ 文件首选markdown。

来自Roslyn项目的简单markdown徽章:

[![Build Status]([http://dotnet-ci.cloudapp.net/job/roslyn_master_win_dbg_unit32/badge/icon)](http://dotnet-ci.cloudapp.net/job/roslyn_master_win_dbg_unit32/)](http://dotnet-ci.cloudapp.net/job/roslyn_master_win_dbg_unit32/badge/icon)](http://dotnet-ci.cloudapp.net/job/roslyn_master_win_dbg_unit32/))

[![Join the chat at [https://gitter.im/dotnet/roslyn](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dotnet/roslyn?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)](https://gitter.im/dotnet/roslyn](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dotnet/roslyn?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge))

自动化解决方案结构验证

即使你已经把我们在这篇文章中讨论到的配置都设置好了,很快你其中一个开发人员可能就会改变他们并把修改提交到源代码版本控制。 有时是因为失误而引起的,并且通常这些修改在代码审查时不会被注意到。除却这些意外外,我们应该关注以下这些公共的错误:
+ 错误引用:当某个人引用了一个本地的程序集而其他人可能没有时,或者当某个人已经把文件从硬盘上删除了,而链接到这个 文件指向仍保留在.csproj中。这肯定会中断构建,但若修改已被推送后可能为时已晚。这一点对于静态网页文件尤其置关重要, 而你又不能在构建过程中进行校验。
+ 命名一致性:像StyleCop这样的工具可以控制C#源代码,但是没有工具可以为项目文件或程序集属性强制规则。一个好的例子就 是:我们想把项目的命名与输出的程序集的名称匹配起来,并且我们希望项目名称有公共的前缀如MyCompany.MyProduct

我发现在代码审查里查看这些错误是容易出错的而且应该自动化。所以我写了一个可以执行这些的工具并且可能验证解决方案的一致性。 请看SolutionInspector。这是开源代码并且以MIT许可发布。你可以通过源代码 来构建或者通过NuGet来安装:

Install-Package SolutionInspector  

这个工具会遍历整个解决方案的结构并应用很多验证规则。规则通过XML文件来配置,与其他解决方案文件放置在一起。为了控制每一个 项目基准的设置,你简单地把相同的文件但不同的配置添加到对应的项目目录即可。

默认情况下不需要任何配置文件。在这种情况下,该工具将应用全部的规则并把全部的问题输出到控制台。

以下是配置文件示例:

<?xml version="1.0" encoding="utf-8"?>  
<Settings xmlns:xsi="[http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">](http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">)

<SolutionSettings>  
    <MinSolutionFormatVersion>12.00</MinSolutionFormatVersion>
    <MaxSolutionFormatVersion>12.00</MaxSolutionFormatVersion>
    <DetectMissingFiles>true</DetectMissingFiles>
    <ProjectNamePrefix>MyCompany.MyProduct.</ProjectNamePrefix>
    <ProjectNameIsFileName>true</ProjectNameIsFileName>
    <IgnoredProjects>
        AVerySpecialProject1;
        AVerySpecialProject2;
    </IgnoredProjects>
</SolutionSettings>

<ProjectSettings>  
    <DetectMissingFiles>true</DetectMissingFiles>
    <AllowBuildEvents>true</AllowBuildEvents>
    <AssemblyNameIsProjectName>true</AssemblyNameIsProjectName>
    <RootNamespaceIsAssemblyName>true</RootNamespaceIsAssemblyName>
    <RequiredImports>StyleCop.MSBuild.Targets</RequiredImports>
    <Properties>
        <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
        <StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
    </Properties>
</ProjectSettings>

</Settings>  

虽然上面的设置都是相当具有自描述性的,但我还是准备想解释一下其中的某些配置:
+ MinSolutionFormatVersion / MaxSolutionFormatVersion将防止你们开发人员切换Visual Studio版本。 + DetectMissingFiles对于静态网页内容和其他解决方案或项目中非源代码文件是非常有用的。
+ AllowBuildEvents可以防止添加可能无作为的自定义构建事件。 + Properties是最灵活的元素:你可以检查所需要值的任何属性,不管是已知属性还是自定义属性。

结论

我们已经回顾了几个当开启一个新的项目时你可以应用的标准实践,配置文件和项目设置。在一开始就做这些可以减少未来技术债务以 及使得你的项目源代码看起来更优雅和专业。对于开源项目这些尤其重要,因为任何贡献者可以通过检查解决方案配置和项目文件来了 解你的期望。

dogstar

一位喜欢翻译的开发人员,艾翻译创始人之一。

广州