项目升级 (NETCore)指南
以 Ctrip.OpenService.Api 项目为例
- 确保安装 .NET Core 3.1
- 确保项目所有的依赖包都有 .NETCore 的版本。CNB.Common / CNB.DevFramework.Cache 这两个依赖包,已经支持 .NETCore
- 进入项目文件夹
- 将 Ctrip.OpenService.Api.csproj 重命名为 Ctrip.OpenService.Api.csproj_bak
- 用文本编辑器新建文件 Ctrip.OpenService.Api.csproj , 保存到原目录。
- 在新项目中, 排除 Properties/AssemblyInfo.cs
- Core 项目默认会把文件夹内的所有文件都包含进项目中, 要把不在TFS中的文件都删掉, 避免产生错误.
- 新的 csproj 文件内容及说明如下所示
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Globals">
<SccProjectName>SAK</SccProjectName>
<SccProvider>SAK</SccProvider>
<SccAuxPath>SAK</SccAuxPath>
<SccLocalPath>SAK</SccLocalPath>
</PropertyGroup>
<PropertyGroup>
<!--
生成多个目标版本, 可以是 netstandard2.0, net45, net451, net461 等.
提供 net452 的版本,是因为当前主项目是 net452 的,要兼容老的项目才提供这个版本的
-->
<TargetFrameworks>netstandard2.1;net452;</TargetFrameworks>
<!--指定命名空间,这里是为了兼容代码,如果不设置,新添加的文件的命名空间就变了-->
<!-- <RootNamespace>Ctrip.OpenService.Api</RootNamespace> -->
<!--指定DLL名称,这里是为了和老的DLL兼容,DLL 就会改名了-->
<!-- <AssemblyName>Ctrip.OpenService.Api</AssemblyName> -->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeSymbols>true</IncludeSymbols>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<!--为false 不会生成版本信息, 注意把 Properties/AssemblyInfo.cs 文件排除-->
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<!--CPS_SUP.Change, Version=0.0.0.11, Culture=neutral, PublicKeyToken=null 中的 Version-->
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<!--无用-->
<!--<AssemblyFileVersion>5.0.0.3</AssemblyFileVersion>-->
<!--文件->属性->文件版本-->
<FileVersion>2.0.0.0</FileVersion>
<PackageTags>CNBooking</PackageTags>
<!--NUGET 包版本-->
<PackageVersion>2.0.0.0</PackageVersion>
<Authors>xling</Authors>
<LangVersion>8.0</LangVersion>
<!--文档路径-->
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<!--
不同的 Framework 的依赖版本不一样,
比如 StackExchange.Redis 1.2.6 以上的版本只支持 .NET 4.6.1 以上的版本,
所以在 net451 最高能使用的版本是 1.2.6
-->
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="CNB.Common" Version="0.0.0.45-beta" />
<PackageReference Include="CNB.DevFrame.Cache" Version="5.0.0.5" />
<PackageReference Include="protobuf-net" Version="2.4.0" />
<PackageReference Include="StackExchange.Redis" Version="2.1.58" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<!--要确保兼容包(NET452)里的依赖版本和 packages.config 里的版本是一致的 -->
<!--如果多个版本都有相同的依赖,可以用 OR-->
<!--<ItemGroup Condition=" '$(TargetFramework)' == 'net451' OR '$(TargetFramework)' == 'net452'">-->
<ItemGroup Condition=" '$(TargetFramework)' == 'net452'">
<PackageReference Include="CNB.Common" Version="0.0.0.45-beta" />
<PackageReference Include="CNB.DevFrame.Cache" Version="5.0.0.5" />
<PackageReference Include="protobuf-net" Version="[2.0.0.668]" />
<PackageReference Include="StackExchange.Redis" Version="[1.2.6]" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<!--因为上面设置了 GenerateAssemblyInfo = true, 会自动生成 AssemblyInfo , 所以这里要把 Properties\AssemblyInfo.cs 排除 -->
<ItemGroup>
<Compile Remove="Properties\AssemblyInfo.cs" />
</ItemGroup>
<!--<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>-->
</Project>
-
CNB.Common 中的 RequestHelper 使用的是 HttpWebRequest, 天生存在缺陷, 不推荐在继续使用,请使用 System.Net.HttpClient 代替。
- HttpClient 应该使用全局的, 不应该每次都新实例.
- 使用 HttpClient 后, 是否使用代理/代理地址, 不能在动态切换, 在程序启动的时候,就必须指定.
- HttpClient 的超时不方便调整, 但是可以按以下方法变通:
/// <summary> /// 初始化 /// </summary> /// <param name="opt"></param> public static void Init(ApiClientOption opt) { if (!Clients.ContainsKey(opt.ApiID)) { var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip, UseProxy = opt.UseProxy, Proxy = !opt.UseProxy ? null : new WebProxy(opt.ProxyAddress), }; var hc = new HttpClient(handler) { BaseAddress = new Uri(opt.BaseUri), //这里指定超时为无限, 在 BaseMethod 里的 Timeout 默认为 100 秒. //因为这个地方不方便自定义 Timeout = Timeout.InfiniteTimeSpan }; var client = new ApiClient() { Option = opt, HttpClient = hc }; Clients.TryAdd(opt.ApiID, client); } } ... ... public abstract class BaseMethod<T> { /// <summary> /// 默认 100 秒 /// </summary> public int? Timeout { get; set; } = 100000; .... .... try { using (var cts = new CancellationTokenSource()) using (var content = new StringContent(json, Encoding.UTF8, "application/json")) using (var msg = new HttpRequestMessage() { Content = content, Method = HttpMethod.Post, RequestUri = new Uri(client.Option.BaseUri), }) { if (this.Timeout > 0) cts.CancelAfter(this.Timeout.Value); msg.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); using (var rsp = await client.HttpClient.SendAsync(msg, cts.Token)) { if (rsp.StatusCode == HttpStatusCode.OK) result = await rsp.Content.ReadAsStringAsync(); } } } catch (TaskCanceledException e) { throw new TimeoutException("请求超时"); }- 即
HttpClient.Timeout设置为无限等待Timeout.InfiniteTimeSpan; 使用CancellationTokenSource(cts.CancelAfter(this.Timeout.Value)) 来控制超时时间.











网友评论