我挑了一个先前写的小工具,来试试看把它从原本的 .NET Framework 4.5 改成 .NET Core 2.0 平台会碰到那些问题。移转过程顺便引入第三方套件来改进代码。这里也顺便记一下那些好用的套件。
简介
挑选来移转至 .NET Core 2 的项目是我先前已经放在 GitHub 的 Chinese-Converter。这是个命令行工具,可以将指定的档案进行简繁/繁简中文的转换。
原本我是以偷懒的作法,调用 Microsoft Word 来帮我做第一轮的简繁转换,然后再用我自己维护的简繁对应辞库进行第二轮的补强修正。既然 .NET Core 的重点在于跨平台,那么自然就得去除对 MS Word API 的依赖。于是,除了修改 csproj 项目的内容,我还得修改一些代码。长话短说:我打算用(新?)同文堂的辞库。
关于同文堂的简繁对应辞库,我在 GitHub 上面至少找到三个。于是我又在我的项目里面增加一个工具:MergePhrase,用来合并从各方搜集来的简繁词汇对照档。目标是集成成单一档案,作为 ChineseConverter 的自带辞典,然后再以其他行业的术语辞库作为额外的补强。
关于 ChineseConverter 的部分说得够多了。看一下跟 .NET Core 有关的部分吧。摘要如下:
- 修改 .csproj 档案
- 使用 Serilog 来输出 log
- 部署
修改 .csproj 档案
ChineseConverter 原本是针对 .NET Framework 4.5 来编译的。现在要改成针对 .NET Core 2.0 平台。
我用 Visual Studio 2017 开启项目的「属性」,在「Target framework」下拉列表里面只会出现 .NET Framework 各版本,而没有 .NET Core 的选项。我的直觉反应就是去手动修改 .csproj 档案。(也许有工具可以处理这件工作?)
注:这里纯粹是描述我对这个项目的做法,不见得适用所有类型的 .NET 项目。
首先,把开头的这几行:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B395A62E-B988-4370-AB66-D76A4FC49C9E}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ChineseConverter</RootNamespace>
<AssemblyName>tscc</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
替换成:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>ChineseConverter</RootNamespace>
<AssemblyName>tscc</AssemblyName>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
然后把包含 <Compile Include>
项目的 <ItemGroup>
区块整个删除,例如:
<ItemGroup>
<Compile Include="TSChineseDictionary.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TSChineseConverter.cs" />
</ItemGroup>
此时用 Visual Studio 2017 开启项目,看一下项目属性里面的 Target framework,竟然变成了 .NET Framework 4.0。
后来我把 .csproj 里面的这行删除:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
然后用 Visual Studio 2017 开启项目,Target framework 就变成 .NET Core 2.0 了。如下图:
接着尝试建置项目,出现一些编译错误。根据错误讯息所提供的线索,我删除了 AssemblyInfo.cs,然后开启项目的「属性」,切换至「Package」,并输入套件的名称、版本等数据:
到这个步骤做完,这个项目就可以建置成功了。
剩下的工作,就是把一些不兼容于 .NET Core 2 的套件或代码改掉。在这当中,我移除了对 Microsoft.Office.Interop.Word.dll 的依赖,同时加入以下套件:
上述套件,Json.NET 和 Command Line Parser 的 .NET Core 版本在用法上跟以前没有明显差异,网络上可以找到很多文章。至于 Serilog 的部分,我尝试把它跟 .NET 的 dependency injection API 接在一起用,所以就顺便在此纪录一下 Serilog 的用法。
使用 Serilog 来输出 log
这里会用到的套件/组件有:
- Microsoft.Extensions.DependencyInjection 2.1.0-preview-final
- Serilog 2.7.1-dev-00960
- Serilog.Extensions.Logging 2.0.2
- Serilog.Sinks.File 4.0.1-dev-00795
Step 0: 建立一个 .NET Core Console 应用程序项目
Step 1: 加入套件
使用 NuGet Manager 或下列指令来安装所需之套件:
Install-Package -IncludePrerelease Microsoft.Extensions.DependencyInjection Install-Package -IncludePrerelease Serilog Install-Package -IncludePrerelease Serilog.Extensions.Logging Install-Package -IncludePrerelease Serilog.Sinks.File
完成后,项目的 .csproj 档案里面会加入以下内容:
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0-preview1-final" />
<PackageReference Include="Serilog" Version="2.7.1-dev-00960" />
<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.1-dev-00795" />
Step 2: 修改 Program.cs
在 Main
函式中加入
// 设定 DI
var serviceProvider = new ServiceCollection()
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true))
.BuildServiceProvider();
// 建立 logger
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.File(opts.LogFileName, Serilog.Events.LogEventLevel.Information)
.CreateLogger();
其中第二行代码的 ServiceCollection 是来自 Microsoft.Extensions.DependencyInjection。这些代码主要是参考自 Serilog 官方范例,用法挺简单。
由于我只需要把 log 写入至档案,所以这里使用了 .WriteTo.File(...)
。Serilog 还支持多种输出目标/格式,需要时可参阅官方文件。
Step 3: 写 log
一旦完成了 Serlog 的初始化设定,接着就可以直接通过 Log
类型来输出 log 字符串了。例如:
Log.Information("应用程序开始执行。");
Log
类型有个静态属性: Logger
,其类型为 ILogger
。由此可见,我们也可以把 Log.Logger 对象注入到其他类型。底下是「构造函数注入」的例子,直接取自 ChineseConverter 项目:
var dict = new TSChineseDictionary(Log.Logger);
在 TSChineseDictionary.cs
里面,构造函数需要接受一个 ILogger 参数,如下:
public class TSChineseDictionary
{
public TSChineseDictionary(ILogger logger)
{
_logger = logger;
}
}
注:Serilog 的一大特色是能够输出「结构化 log 讯息」方面的能力,这里没有用到。
部署
程序大致改好之后,我是通过 Solution Explorer 里面对项目名称点右键、选择 Publish 的方式来产生部署所需的档案。默认情况下,Visual Studio 会将此应用程序执行时所需要的档案放在项目的 bin\Release\PublishOutput\ 目录下。
注:如果开启命令窗口,将现行目录切换至项目的 bin\Debug\netcoreapp2.0\,然后用
dotnet myapp.dll
的方式来执行应用程序,可能会因为缺少其他相依组件而无法执行。
小结
由于我的这个 console app 项目只是个小工具而已,程序并不复杂,需要的第三方套件也都能找到支持 .NET Core 2 的版本,所以整个移转过程算是蛮顺利的。
文中没有提到的是,同一个 solution 里面还有一个单元测试项目。我同样把它改成 target .NET Core 2.0,但采用了不同的方法:这次我是重新建立一个新的单元测试项目,再把档案加进去。我觉得这样反而简单、干脆。
完工以后,又上网搜了一下,发现也有类似的心得分享,而且更详细。附在下方的〈延伸阅读〉。
再补充一下 ChineseConverter 的进度:目前已经把网络上搜集来的三个简繁对应辞库档案合并好了。由于转换的核心代码尚未改完,故仍放在工作分支,尚未合并至 master。此间,亦曾与同文堂的开发团队成员接触,询问辞库的问题。也许将来有机会再把字典文件的部分进一步完善吧。
Happy coding!