首页 文章详情

使用 Directory.Build 来消除项目文件中的重复配置

DotNet NB | 215 2021-10-26 17:33 0 0 0
UniSMS (合一短信)

使用 Directory.Build 来消除项目文件中的重复配置

Intro

如果解决方案里的项目比较多的话,往往会有很多重复的项目属性,通常我们可以使用独立的 props 属性文件来配置公用的属性,而一般的属性文件都需要手动的 Import 到项目文件中,而 MSBuild 支持自动导入我们要介绍的 Directory.Build.props中的配置,所以通常我们可以使用 Directory.Build.props 来减少项目中重复的属性

DirectoryBuild

什么是 Directory.Build 呢?

Directory.Build 文件是放在某一个目录下,其中的配置针对这个目录下所有的项目都生效,并且 MSBuild 运行的时候会自动引入,不需要显式地在项目文件中引入,主要分成 Directory.Build.propsDirectory.Build.targets 两类,在很多开源项目包括微软的开源项目都有用到这个来简化项目配置,.props 文件是属性文件,通常定义公用的属性,导入时机较早,项目中的定义可以覆盖掉其中的配置,.targets 是目标文件,通常定义一些自定义的 MSBuild Task,导入时间稍后,可以用来覆盖项目文件中的定义

通常我只是用到 Directory.Build.props 来配置公用的属性

下面是一个项目文件中的一部分:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>

上面 TargetFramwork/Nullable/ImplicitUsings 是一个 MSBuild 属性,需要声明在 PropertyGroup

如果一个解决方案中有很多个项目,那么这些可能就都是重复项了,这时候使用 Directory.Build.props 就会比较方便,比如说 ImplicitUsing 这一属性,这个属性是用来启用 .NET 6 里的隐式命名空间引用的,但是从 .NET 6 RC1 默认是禁用的,需要显式声明 <ImplicitUsings>enable</ImplicitUsings> 来启用,如果项目里的项目都是需要启用的,那就可以直接声明在 Directory.Build.props 中,放在项目根目录下,如下所示:

<Project>
    <PropertyGroup>
        <LangVersion>latest</LangVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>
</Project>

除了 PropertyGroup 之外,我们也可以定义 <ItemGroup>,下面就是一个示例:

<Project>
    <PropertyGroup>
        <LangVersion>latest</LangVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>
    <ItemGroup>
        <Using Include="WeihanLi.Common"/>
        <Using Include="WeihanLi.Common.Helpers"/>
        <Using Include="WeihanLi.Extensions"/>
    </ItemGroup>
</Project>

这样我们可以把公用的命名空间定义在 Directory.Build.props 上, MSBuild 会按目录结构依次寻找上层的 Directory.Build.props,找到第一个之后就会停止再往上寻找,下面是一个示例

c:\users\username\code\test\case1
c:\users\username\code\test
c:\users\username\code
c:\users\username
c:\users
c:\

如果项目较多可以使用多层 Directory.Build.props,下面是一个示例:

\
  MySolution.sln
  Directory.Build.props     (1)
  \src
    Directory.Build.props   (2-src)
    \Project1
    \Project2
  \test
    Directory.Build.props   (2-test)
    \Project1Tests
    \Project2Tests

所有项目 (1) 的通用属性、src 项目 (2-src) 的通用属性,以及 test 项目 (2-test) 的通用属性

前面我们提到过,MSBuild 对于指定项目找到第一个 Directory.Build.props 就会停止再向上寻找 Directory.Build.props 如果要同时使用多级 Directory.Build.props 则需要显式导入上层的 Directory.Build.props

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

Sample

下面我们来看一些使用示例吧

Sample1

在稍微复杂一些项目,往往会引用很多的 Nuget 包,而一些项目的版本可能是有关系的,有些包的版本是一致的,此时我们可以考虑定义一个属性,而在包版本的地方则引用这个属性

项目根目录下 Directory.Build.props 内容

<PropertyGroup>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <EFVersion>6.0.0-rc.1.*</EFVersion>
</PropertyGroup>

项目文件中内容:

<ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(EFVersion)" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(EFVersion)" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(EFVersion)" />
</ItemGroup>

这样我们如果需要更新 EF 的版本,我们只需要更新 Directory.Build.props 文件中的 EFVersion 这个属性就可以了,详细配置可以参考:https://github.com/WeihanLi/WeihanLi.EntityFramework

Sample2

我们再来看一个多个层级使用的示例,项目结构如下所示:

Directory.Build.props
--src
--tests
--Directory.Build.props
----UnitTest
----IntegrationTest

项目根目录下 Directory.Build.props,主要定义 package 相同的属性配置以及启用可控引用类型,隐式命名空间和公用的命名空间

<Project>
    <PropertyGroup>
        <LangVersion>latest</LangVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <PackageIcon>icon.png</PackageIcon>
        <PackageTags>HTTPie http https curl rest</PackageTags>
        <GenerateDocumentationFile>false</GenerateDocumentationFile>
        <PackageLicenseExpression>MIT</PackageLicenseExpression>
        <RepositoryType>git</RepositoryType>
        <RepositoryUrl>https://github.com/WeihanLi/dotnet-httpie</RepositoryUrl>
        <PackageProjectUrl>https://github.com/WeihanLi/dotnet-httpie</PackageProjectUrl>
        <Product>dotnet-HTTPie</Product>
        <Authors>WeihanLi</Authors>
        <PackageReleaseNotes>https://github.com/WeihanLi/dotnet-httpie/tree/main/docs/ReleaseNotes.md</PackageReleaseNotes>
        <Copyright>Copyright 2021 (c) WeihanLi</Copyright>
    </PropertyGroup>
    <ItemGroup>
        <Using Include="WeihanLi.Common"/>
        <Using Include="WeihanLi.Common.Helpers"/>
        <Using Include="WeihanLi.Extensions"/>
    </ItemGroup>
</Project>

tests 目录下 Directory.Build.props,定义了测试项目公用的命名空间,并引入了上一层 Directory.Build.props 中的属性

<Project>
    <Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
    <ItemGroup>
        <Using Include="FluentAssertions"/>
        <Using Include="Xunit"/>
    </ItemGroup>
</Project>

IntegrationTest 项目文件,引入自己需要的命名空间引用

<ItemGroup>
    <Using Include="HTTPie"/>
    <Using Include="HTTPie.Implement"/>
    <Using Include="HTTPie.Middleware"/>
    <Using Include="HTTPie.Models"/>
    <Using Include="HTTPie.Utilities"/>
</ItemGroup>

具体配置可以参考:https://github.com/WeihanLi/dotnet-httpie

More

使用好 Directory.Build.props 可以简化项目配置,使得项目更容易维护,如果还没用起来的可以尝试一下哈

References

  • https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?WT.mc_id=DT-MVP-5004222&view=vs-2019
  • https://github.com/WeihanLi/SparkTodo/blob/master/Directory.Build.props
  • https://github.com/WeihanLi/SamplesInPractice/tree/master/net6sample
  • https://github.com/WeihanLi/WeihanLi.EntityFramework/blob/dev/Directory.Build.props
  • https://github.com/WeihanLi/dotnet-httpie/blob/dev/Directory.Build.props
  • https://github.com/WeihanLi/dotnet-httpie/blob/dev/tests/Directory.Build.props


推荐阅读:
Kubernetes全栈架构师(Kubeadm高可用安装k8s集群)--学习笔记
.NET 云原生架构师训练营(模块一 架构师与云原生)--学习笔记
.NET Core开发实战(第1课:课程介绍)--学习笔记

点击下方卡片关注DotNet NB

一起交流学习

▲ 点击上方卡片关注DotNet NB,一起交流学习

请在公众号后台


回复 【路线图】获取.NET 2021开发者路线图
回复 【原创内容】获取公众号原创内容
回复 【峰会视频】获取.NET Conf开发者大会视频
回复 【个人简介】获取作者个人简介
回复 【年终总结】获取作者年终总结
回复 加群加入DotNet NB 交流学习群

长按识别下方二维码,或点击阅读原文。和我一起,交流学习,分享心得。


good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter