9

从以前的项目格式迁移到 VS2017 新项目格式

 3 years ago
source link: https://lindexi.gitee.io/post/%E4%BB%8E%E4%BB%A5%E5%89%8D%E7%9A%84%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E8%BF%81%E7%A7%BB%E5%88%B0-VS2017-%E6%96%B0%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
从以前的项目格式迁移到 VS2017 新项目格式

以前的项目格式使用的是 csproj 的格式,但是 .net core 支持使用 project.json 格式的项目文件,后来还是决定不使用这个格式。 VS2017 的项目格式更好读、更简单而且减少了 git 冲突。 本文来告诉大家如何从 VS2015 和以前的项目格式修改为 VS2017 项目格式。当前对新项目格式的命名是 SDK Style 风格的 csproj 项目文件格式。而在 VS2015 和以前的项目格式是 Franken-proj 格式

在迁移之前,我需要告诉大家,现在是2018年1月15日,最新的项目格式只有对下面的项目支持 现在是 2020年12月 好像啥项目都支持了

  • class library projects 类库项目
  • console apps 控制项目
  • ASP.NET Core web apps asp 项目
  • .NET Core .NET Core
  • 还有其他所有的项目

对于 UWP 和 WPF ,有 xaml 的项目是没有很好支持 现在是 2020年12月 好像支持不错

现在很多项目,测试使用的项目都使用新格式,建议在自己用来测试的项目试试

建议从一个测试项目试试,先做好提交,如果失败可以回滚。 不用试试了,我都玩了三年了

如果创建是库项目,那么 csproj 文件只有下面的代码就可以完成了

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net46</TargetFramework>
  </PropertyGroup>
</Project>

如果创建的是控制项目,那么只有下面的代码

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net46</TargetFramework>
  </PropertyGroup>
</Project>

如果创建的是测试项目,那么只有下面的代码

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net46</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
    <PackageReference Include="xunit" Version="2.2.0" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
  </ItemGroup>
</Project>

如果是 WinForms 或 WPF 项目,那么只有下面的代码

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFrameworks>netcoreapp3.1;net5-windows</TargetFrameworks>
    <UseWPF>true</UseWPF>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

</Project>

如果想知道新格式和之前的区别,如何从以前的格式迁到新的格式,请继续阅读本文

下面从项目的第一行开始来告诉大家如何一步步迁移。当前还没有一个可以做到自动化的迁移工具,但是有一个快捷的方式是重新新建项目,然后一点点将原有项目的内容通过在 VS 上可视化的步骤加回来。本文会告诉大家的是如何编辑 csproj 文件,而不是在 VS 上通过点一点完成,本文更适合高级的开发者

原来的第一行的代码大概内容如下

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

新的格式的第一行内容如下

<Project Sdk="Microsoft.NET.Sdk">

或者对于 WPF 来说是如下格式,关于 WPF 部分放在本文最后

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

从第一行可以看到新的格式的代码比较少,而实际上除了第一行之外,后面的内容有着更大的简化

其实建议大家重新创建一个项目,然后把文件放进去,安装 Nuget 不然需要修改比较多

必须删除的内容

下面的代码必须删除,因为新的 SDK 风格的项目格式不再需要了

<!-- usually at the top of the file -->
<!-- 下面的代码一般在文件的最前 -->
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />

<!-- usually at the bottom -->
<!-- 一般在文件的最后面 -->
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />

还需要删除 AssemblyInfo.cs 文件,不然编译出现 CS0579 重复,请看下面提示内容

    Error CS0579: “System.Reflection.AssemblyCompanyAttribute”特性重复 (1, 1)

特性文件不删除也可以,请使用解决从旧格式的 csproj 迁移到新格式的 csproj 格式 AssemblyInfo 文件值重复问题的方法

需要修改平台框架的代码

这是以前的代码

<PropertyGroup>
  <!-- ... -->
  <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>

需要修改为下面的代码

<PropertyGroup>
  <TargetFramework>net452</TargetFramework>
</PropertyGroup>

如果是 v4.5.2 ,请修改为 net452 ,如果是 v4.6 就修改为 net46 详细请看 dotnet 新项目格式与对应框架预定义的宏

现在新的格式可以使用通配添加文件,例如在文件夹的所有的代码都需要添加,可以使用这个方式

<Compile Include="lindexi\*.cs" />

如果啥都不做,默认的 SDK 风格的项目格式已经包含了如下的引用

<!-- the defaults -->
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />

所以添加的 cs 文件都会添加到编译,需要删除这个代码才可以不编译一些文件,也就是原先写的添加文件都需要删除,如果不想删除可以使用 EnableDefaultCompileItems 设置不添加 cs 文件

    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>

对 WPF 项目需要额外设置 EnableDefaultPageItems 不包含 Page 解决

    <EnableDefaultPageItems>false</EnableDefaultPageItems>

更多默认的行为请看 Roslyn 禁止 sdk style csproj 默认引用 Compile 代码文件

之前的方式需要添加很多代码,如引用 ClassLibrary1 的项目,需要写下面的代码

<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj">
  <Project>{2C7DF870-5B35-49EF-963D-EE1E72C3177E}</Project>
  <Name>ClassLibrary1</Name>
</ProjectReference>

Project 这个可以表示这是一个类库或一个其他的项目,因为新的项目不需要这个,所以在新建文件的时候就不知道给哪个项目,这是比较差的

新的格式就需要下面的代码,不需要使用 Project 和 Name 这两个元素

<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" />

如果引用的项目有依赖,以前的格式需要把引用写在文件,现在不需要添加引用

假如有 A 引用 B ,B 引用 C ,那么之前的A项目文件就是这样

<ProjectReference Include="..\ProjectB\ProjectB.csproj">
  <Project>{A900C843-8340-421B-B4F0-6C65A0D093C4}</Project>
  <Name>ProjectB</Name>
</ProjectReference>
<ProjectReference Include="..\ProjectC\ProjectC.csproj">
  <Project>{871AC142-FC46-49F5-A5E0-90436648B9C5}</Project>
  <Name>ProjectB</Name>
</ProjectReference>

现在的文件格式不需要写引用的依赖,只需要写入最顶层的依赖就可以了

<ProjectReference Include="..\ProjectB\ProjectB.csproj" />

而原先存在的一些引用,如 System.Net.Http 等可以使用 dotnet 新 sdk style 项目格式的一些命名空间和引用 的方法引用

之前的 Nuget 引用需要添加 packages.config 和 csproj 才可以使用,现在的 Nuget 4 可以直接在 csproj 添加引用

这是之前的格式

<Import Project="..\..\packages\xunit.runner.visualstudio.2.2.0\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\..\packages\xunit.runner.visualstudio.2.2.0\build\net20\xunit.runner.visualstudio.props')" />

<ItemGroup>
  <None Include="packages.config" />

  <Reference Include="MySql.Data, Version=6.9.9.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
  <HintPath>..\..\packages\MySql.Data.6.9.9\lib\net45\MySql.Data.dll</HintPath>
  <Private>True</Private>
</Reference>
</ItemGroup>

新的格式写引用,不需要 packages.config 文件,这样减少了找不到nuget的坑,下面代码就是新的格式,可以看到代码减少1/2

<ItemGroup>
  <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
  <PackageReference Include="MySql.Data" Version="6.9.9" />
</ItemGroup>

这个格式可以在git冲突比较容易看到哪里需要修改,所以解决冲突很简单

在 nuget 2 的引用,如果引用了包A,他引用了 B 库,那么就需要在 packages.config 引用写了这几个项目

<?xml version="1.0" encoding="utf-8"?>
<!-- in packages.config -->
<packages>
  <package id="A" version="2.2.0" targetFramework="net452" />
  <package id="B" version="2.0.1" targetFramework="net452" />
</packages>

现在 Nuget 4 需要写引用的库,不需要写他的引用,所以可以减少代码,添加了id和版本就好

<ItemGroup>
  <PackageReference Include="A" Version="2.2.0" />
</ItemGroup>

如果在迁移过程遇到诡异的问题,请看将 WPF、UWP 以及其他各种类型的旧样式的 csproj 文件迁移成新样式的 csproj 文件 - walterlv

删除多余文件

现在 VisualStudio 2017 项目格式不需要使用 AssemblyInfo 包含一些值,所以现在编译 VisualStudio 会发现重复定义了一些值,需要删除 Properties/AssemblyInfo.cs 文件。

如果只有需要定义一些全局的特性,那么直接新建一个类写就好。

如果不想删除,请设置 GenerateAssemblyInfo 属性

    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

详细请看 解决从旧格式的 csproj 迁移到新格式的 csproj 格式 AssemblyInfo 文件值重复问题

删除的时候小心属于 WPF 的 ThemeInfo 特性和 COM 不能删除

[assembly: ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
    //(在页面或应用程序资源词典中 
    // 未找到某个资源的情况下使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
    //(在页面、应用程序或任何主题特定资源词典中
    // 未找到某个资源的情况下使用)
)]

输出注释文档

如果需要输出注释文档文件,也就是 xx.xml 注释文件,在以前的代码是在属性页面,点击生成xml选项就可以生成的注释文档,现在新的格式和之前有些不一样,可以通过添加下面的代码生成xml注释文档文件,请看代码

  <PropertyGroup>
    <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
  </PropertyGroup>

这里的$(Configuration)就是用到了宏,他会替换当前编译的是 Debug 还是 release ,所以对于所有的项目都可以使用这个来生成 xml 注释文档文件

或者使用 OutputPath 相同的文件夹,因为可能是自己定义了 OutputPath 下面代码就自己修改了不在当前的文件夹

  <PropertyGroup>
    <OutputPath>..\..\bin\$(Configuration)\</OutputPath>
    <DocumentationFile>$(OutputPath)$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
  </PropertyGroup>

注意这时需要让 OutputPath 在 DocumentationFile 之前,否则拿到的值是默认的值

更简易的方法,可以无视路径的方法是使用 GenerateDocumentationFile 属性

  <PropertyGroup>
      <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>

将 XML 输出到 NuGet 包的方法请看 Roslyn 在 NuGet 包中放注释 xml 文件的方法

如果还有其他的文件也需要放入 NuGet 包,请看 Roslyn 打包自定义的文件到 NuGet 包

如果需要同时打包出 dotnet standard 和 dotnet framework 的包,就需要使用下面的方法。

因为现在存在一些项目是使用多个开发框架,这时就需要修改TargetFrameworkTargetFrameworks也就是写为复数的TargetFrameworks,把里面的一个框架修改为多个,请看下面

  <PropertyGroup>
    <TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
  </PropertyGroup>

如果想知道每个平台的缩写,请看Target frameworks 或我的博客 dotnet 新项目格式与对应框架预定义的宏

.NET Standard

  • netstandard1.0
  • netstandard1.1
  • netstandard1.2
  • netstandard1.3
  • netstandard1.4
  • netstandard1.5
  • netstandard1.6
  • netstandard2.0

.NET Core

  • netcoreapp1.0
  • netcoreapp1.1
  • netcoreapp2.0
  • netcoreapp2.1

.NET Framework

  • net11
  • net20
  • net35
  • net40
  • net403
  • net45
  • net451
  • net452
  • net46
  • net461
  • net462
  • net47
  • net471
  • net472

Universal Windows Platform

  • uap [uap10.0]
  • uap10.0 [win10] [netcore50]

使用条件判断

因为在多个框架,存在一些框架不能引用的库,而在一些框架需要这些库,如 ValueTuple ,就需要在引用的时候添加条件

添加条件可以使用这样的代码Condition=" '$(TargetFramework)' == 'net40' 把 net40 修改为你希望的框架就在指定的框架引用库。

  <ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
    <Reference Include="System.Net" />
  </ItemGroup>

如果通过条件还可以在某些框架引用或不引用某些文件。

在代码可以通过默认的宏来判断当前是哪个框架,默认的宏就是上面的缩写的大写

    static void Main()
    {
#if NET40
        Console.WriteLine("Target framework: .NET Framework 4.0");
#elif NET45  
        Console.WriteLine("Target framework: .NET Framework 4.5");
#endif
    }

不同框架的宏请看下面

.NET Framework

  • NET20
  • NET35
  • NET40
  • NET45
  • NET451
  • NET452
  • NET46
  • NET461
  • NET462
  • NET47
  • NET471
  • NET472

.NET Standard

  • NETSTANDARD1_0
  • NETSTANDARD1_1
  • NETSTANDARD1_2
  • NETSTANDARD1_3
  • NETSTANDARD1_4
  • NETSTANDARD1_5
  • NETSTANDARD1_6
  • NETSTANDARD2_0

.NET Core

  • NETCOREAPP1_0
  • NETCOREAPP1_1
  • NETCOREAPP2_0
  • NETCOREAPP2_1

更多宏细节请看 dotnet 新项目格式与对应框架预定义的宏

参见:让一个 csproj 项目指定多个开发框架 - walterlv

迁移 WPF 项目

如果需要迁移 WPF 项目,那么先记下自己的引用,最好是截图,然后卸载项目。编辑项目文件,使用下面的代码代替

<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
  <PropertyGroup>
    <LanguageTargets>$(MSBuildToolsPath)\Microsoft.CSharp.targets</LanguageTargets>
    <TargetFrameworks>net45;</TargetFrameworks>
    <OutputType>WinExe</OutputType>
  </PropertyGroup>
 
  <ItemGroup>
    <Compile Update="**\*.xaml.cs">
      <DependentUpon>%(Filename)</DependentUpon>
    </Compile>
    <Page Include="**\*.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>

    <Resource Include="**\*.png" />
    <Resource Include="**\*.jpg" />
    <Resource Include="**\*.cur" />
    <Resource Include="**\*.ps" />
    <None Include="**\*.fx" />
    <None Include="**\*.md" />
    <None Include="**\*.ruleset" />
  </ItemGroup>
 
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml"/>
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
</Project>

保存重新加载,可能需要修改平台,我这里使用 net45 ,大家按照原来的需要修改。

看一下缺少了哪些引用再自己添加。

稍微解释一下上面的代码,在一开始使用的 OutputType 是告诉 VisualStudio 生成一个窗口应用程序。因为默认生成的是 dll ,而在本文上面也告诉大家设置控制台输出是使用下面代码

    <OutputType>Exe</OutputType>

对于 WPF 是窗口程序,如果使用了上面的设置,就会在启动的过程先出现控制台窗口,然后在显示主界面。为了让 WPF 窗口直接显示而不是先显示控制台窗口,需要把上面代码修改为

    <OutputType>WinExe</OutputType>

如果此时提示找不到 App.xaml 请在 csproj 添加下面代码

    <ApplicationDefinition Include="App.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </ApplicationDefinition>

在 WPF 还有很多窗口页面,这些代码使用 xaml 来写,需要把 xaml 和 xaml.cs 对应,所以需要使用下面代码

    <Compile Update="**\*.xaml.cs">
      <DependentUpon>%(Filename)</DependentUpon>
    </Compile>
    <Page Include="**\*.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>

上面这个代码的意思是对于 xaml 文件的编译和折叠,如折叠 Foo.xaml 和 Foo.xaml.cs 文件。现在对于 UWP 项目,使用上面的方法是编译不通过。如果需要支持 UWP 的 VisualStudio 2017 项目格式,请看 新 csproj 对 WPF/UWP 支持不太好?有第三方 SDK 可以用!MSBuild.Sdk.Extras - walterlv。如果不想点击网站,那么就请看代码

<Project Sdk="MSBuild.Sdk.Extras/1.5.4">
  <PropertyGroup>
    <TargetFrameworks>net47;uap10.0</TargetFrameworks>
  </PropertyGroup>
</Project>

现在官方支持使用下面代码迁移 WPF 项目

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

</Project>

核心是 Sdk="Microsoft.NET.Sdk.WindowsDesktop"<UseWPF>true</UseWPF> 两句话

如果需要引用 WinForms 项目,请添加 UseWindowsForms 属性

    <UseWindowsForms>true</UseWindowsForms>

把项目文件修改为上面的代码就可以支持 dotnet 4.7 和 UWP 项目

在新 SDK Style 风格 csproj 项目格式文件使用 WPF 和 WinForms 引用

如上面描述,在添加了 .NET Core 3.1 和以下版本或 .NET Framework 版本,可以使用 Microsoft.NET.Sdk.WindowsDesktop 构建

如果有 .NET 5 的引用支持,请使用 net5.0-windows 框架,于是一个多框架支持的项目格式,同时引用 WPF 和 WinForms 的新 SDK Style 风格的项目格式文件内容可以是如下

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFrameworks>netcoreapp3.1;net5-windows</TargetFrameworks>
    <UseWPF>true</UseWPF>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

</Project>

参见:Old csproj to new csproj: Visual Studio 2017 upgrade guide

将 WPF、UWP 以及其他各种类型的旧样式的 csproj 文件迁移成新样式的 csproj 文件 - walterlv


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/%E4%BB%8E%E4%BB%A5%E5%89%8D%E7%9A%84%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E8%BF%81%E7%A7%BB%E5%88%B0-VS2017-%E6%96%B0%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者前往 CSDN 关注我的主页

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

无盈利,不卖课,做纯粹的技术博客

以下是广告时间

推荐关注 Edi.Wang 的公众号
lindexi%2F201985113622445

欢迎进入 Eleven 老师组建的 .NET 社区
lindexi%2F20209121930471745.jpg

以上广告全是友情推广,无盈利


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK