2020-09-25

2020-09-25  本文已影响0人  gruan

项目升级 (NETCore)指南

以 Ctrip.OpenService.Api 项目为例

  1. 确保安装 .NET Core 3.1
  2. 确保项目所有的依赖包都有 .NETCore 的版本。CNB.Common / CNB.DevFramework.Cache 这两个依赖包,已经支持 .NETCore
  3. 进入项目文件夹
  4. 将 Ctrip.OpenService.Api.csproj 重命名为 Ctrip.OpenService.Api.csproj_bak
  5. 用文本编辑器新建文件 Ctrip.OpenService.Api.csproj , 保存到原目录。
  6. 在新项目中, 排除 Properties/AssemblyInfo.cs
  7. Core 项目默认会把文件夹内的所有文件都包含进项目中, 要把不在TFS中的文件都删掉, 避免产生错误.
  8. 新的 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>


  1. 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)) 来控制超时时间.
上一篇 下一篇

猜你喜欢

热点阅读