热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

.NETStandard中配置TargetFrameworks输出多版本类库

 在.NETStandard.NETCore技术出现之前,编写一个类库项目(暂且称为基

   在.NET Standard/.NET Core技术出现之前,编写一个类库项目(暂且称为基础通用类库PA)且需要支持不同 .NET Framework 版本,那么可行的办法就是创建多个不同版本的项目(暂且称为PB1、PB2、PB3 … PBn)。PB1、PB2、PB3 … PBn项目分别执行下面操作:【添加】–【现有项】–【添加为链接的方式】,将PA项目代码文件添加到各自项目中,如果代码不同,则需要使用#if #else #endif 等标签来判断 .NET Framework 版本。而在.NET Standard/.NET Core技术出现之后,可以通过配置SDK 样式项目中的目标框架来支持一套代码同时输出多版本类库。

  下面以Visual Studio 2019 来演示整个操作过程。

1、新建一个 .NET Standard 类库。


2、填写项目名称


 3、创建完成后,查看“解决方案资源管理器”,项目下面多了一个“依赖项”节点,子节点是SDK,孙子节点是 NETStandard.Library(2.0.3)。


 项目组织方式与传统类库项目的组织方式不同


 4、项目,右键【属性】–>【应用程序】–> “目标框架”默认是 .NET Standard 2.0。


 

也可以修改为其他版本


 

5、编译项目,查看bin –> debug。生成了 netstandard2.0目录


目录里面生成的DLL,这与传统.NET Framework 类型的类库项目生成结果相同。


6、项目,右键 –> “编辑项目文件”



可以看到当前类库默认为 netstandard2.0,而此时其xml标签为 TargetFramework。

如果要支持多版本,则需要做调整,将 TargetFramework 节点修改为 TargetFrameworks,再添加目标版本。

7、配置多目标框架

关于如何指定多目标框架,请参考博客《.NET Standard SDK 样式项目中的目标框架》

 我做的BIMFACE二次开发的接口的目标是支持 .NET Framework4.0、.NET Framework4.5 以及 .NET Core3.1。所以配置了选下3个目标版本


net40;net45;netstandard2.0;


net40;net45;netstandard2.0;

 修改后并保存,Visual Studio 会弹出黄色背景的提示信息。


这里一定要点击【重新加载项目】按钮。重新加载后,依赖项中出现了如下图所示的3个项


 展开每个项查看, 每个版本的程序集对应一个单独的依赖项节点。


8、项目,右键【属性】–>【应用程序】–> “目标框架”被禁用,因为单个项目支持多版本类库,无法一次呈现多个,这是正确的。


 9、重新编译项目,查看bin –> debug,生成了3种不同版本的目标程序集。


 

通过上面的步骤我们已经实现了多版本输出,但是在实际的企业级业务系统开发时情况比较复杂,还需要解决以下几个问题:

1、条件编译

2、引用本地程序集

3、NuGet方式引用程序集

4、XML文档输出

5、编码与DEBUG 调试

6、自动生成内部版本号

7、文件复制

 

下面逐步讲解如何解决以上问题。

这是VS中默认的编译输出目录。

如果需要配置不同的类库输出到不同的位置,也可以自定义配置输出路径实现。

查看项目属性,【生成】–>“输出”–>“输出路径”中输入自定义目录或者点击【浏览】按钮选择一个目录。


填写后,保存项目。项目右键,【编辑项目文件】,csproj文件中自动增加了如下配置,其中 Condition 后面的表达式即是编译条件。OutputPath即是自定义输出目录。


binDebug


binDebug

按照以上方式再复制2份,分别配置 net45 与 netstandard2.0版。完整配置如下:



binDebug



binDebug



binDebug



binDebug



binDebug



binDebug

binDebug 是我自己定义的输出目录,大家可以根据实际需求填写其他目录。


 

 

$(TargetFramework)的条件为 节点中配置的值。


 

 

查看项目属性,【生成】–>“常规”–>“条件编译和符号”中输入自定义内容。选择 “定义DEGUG常数” 与 “定义TRACE常量”,保存项目。

查看csproj文件,在第一个目标版本对应的

配置节点下增加了

TRACE;DEBUG;NET_FULL;TEST;

TRACE;DEBUG;NET_FULL;TEST;

为了做统一配置,将其提取出来



TRACE;DEBUG;RELEASE;NET_FULL;TEST;



TRACE;DEBUG;RELEASE;NET_FULL;TEST;

 

在下图中可以看出由于3个不同的输出类库中所引用的程序集是不同的,那么当编译时,一定是每个类库进行单独编译,这时就就需要通过某种方式告诉编译器当前编译的类库版本是什么,然后添加针对具体版本的第三方程序集引用。


.NET Standard 指定多个目标框架时,可有条件地为每个目标框架引用程序集。

以下库项目面向 .NET Standard (netstandard1.4) 和 .NET Framework(net40 和 net45)的 API。 将复数形式的 TargetFrameworks 元素与多个目标框架一起使用。 为两个 .NET Framework TFM 编译库时,Condition 属性包括特定于实现的包:



netstandard2.0;net40;net45












netstandard2.0;net40;net45










 

下面开始添加引用,点击项目子节点【依赖项】–>【添加程序集引用】


打开如下界面。默认加载的目标框架显示为 .NET Framework 4。


如何才能添加 net45 或者 netstandard2.1 的引用呢?正常来说应该在VS的“引用管理器”界面上提供目标框架的下拉选择框,可以自由切换选择不同的目标框架,但是到目前为止VS没有此功能,我的VS版本信息如下


希望微软在后续VS版本中能增加此功能。

回到csproj编辑界面,可以看到 TargetFrameworks 值第一个为 net40,估计与这个有关系。


通过取巧的方式调整 TargetFrameworks 里的版本先后顺序,保存后,重启VS(我的VS2019是这种情况,需要重启才生效。不知道其他小伙伴们的VS是不是保存后可以自动切换呢?)


再次添加程序集引用,此时加载了 .NET Framework 4.5


 添加一个“System.Net.dll”引用来测试一下


添加后,如下图所示


.NET Framework 4.5 项目中多了“System.Net.dll”引用。但是 .NET Standard 2.0 前面显示黄色警告符合。展开所有依赖项,.NET Framework 4.0 与 .NET Framework 4.5 都已经正确引用。


.NET Standard 2.0 程序及引用有警告。这表示 netstandard2.0 并不知道 System.Net.dll 是什么。

查看.csproj文件


红色框内的配置,表示net40、.net45 和 netstand2.0 都需要“System.Net”引用(即统一配置),而实际只有 net40、.net45 才需要该引用,所以这里我们要使用 Condition 条件,修改如下:


这样只有 .net40 与 .net45 条件下才引用“System.Net.dll”。保存后,发现 netstand2.0 下面的警告标示消失了。

下面演示添加一个多版本都支持的第三方类库,NLog 日志组件,目前最新版本为4.7.5。通过 NuGet 方式添加引用


下图可以看出该组件同时支持 .NET4.0、.NET4.5 以及 .NET Standard 2.0 


点击【安装】


点击【确定】,安装完成后,每一个类库均添加了引用


 查看.csproj文件,添加了如下配置


注意这里是 PackageReference,而之前程序集的是 Reference,而且我们也会发现在VS解决方案管理器中并没有出现 packages.config 文件。默认在 sln 文件的同级也没有创建一个 packages 文件夹。


 而是将dll下载到了C:Users当前登录用户.nuget目录下,这与java的Maven管理方式类似。我的本地路径为:C:UsersSavion.nugetpackages



 

下面再添加一个 netstandard 专有的 nuget 引用 Microsoft.Extensions.DependencyInjection.dll


点击【安装】


点击【确定】


点击【我接受】。

添加完后解决方案中仅有 .NET Standard2.0 中增加了引用。.net40 与 .net45 中没有引用。


 添加完后 csproj文件 会多出如下配置


NuGet 很智能,自动把 Condition 给加好了。

选择项目,点击 属性–>生成,勾选 “XML 文档文件”。默认生成的xml文件名称包含绝对路径,这个名称不是很友好,一般修改为程序集的名称即可


 点击菜单栏上的【保存】按钮。查看.csproj文件新增了如下配置:


 这表示 net40 会生成 xml 文件,将该配置信息复制两份,然后修改 Platform 以及输出路径为 net45 与 netstandard2.0。完整配置如下:



ZCN.NET.BIMFace.SDK.xml
binDebug



ZCN.NET.BIMFace.SDK.xml
binDebug



ZCN.NET.BIMFace.SDK.xml
binDebug



ZCN.NET.BIMFace.SDK.xml
binDebug



ZCN.NET.BIMFace.SDK.xml
binDebug



ZCN.NET.BIMFace.SDK.xml
binDebug

重新编译项目,查看输出目录里面的内容





 其中ZCN.NET.BIMFace.SDK.xml 内容如下


  .netstandard2.0 中多了一个 ZCN.NET.BIMFace.SDK.deps.josn 文件,里面包含了运行时环境以及依赖项等信息


///


/// 判断字符串是否为null、空或者空白
///

///

待判断的字符串
///
public static bool IsNullOrWhiteSpace(this string str)
{
return string.IsNullOrEmpty(str.Trim());
}

///


/// 判断字符串是否为null、空或者空白
///

///

待判断的字符串
///
public static bool IsNullOrWhiteSpace(this string str)
{
return string.IsNullOrEmpty(str.Trim());
}

在.NET4.0及以上框架下使用下面的方式实现

///


/// 判断字符串是否为null、空或者空白
///

///

待判断的字符串
///
public static bool IsNullOrWhiteSpace(this string str)
{
return string.IsNullOrWhiteSpace(str);
}

///


/// 判断字符串是否为null、空或者空白
///

///

待判断的字符串
///
public static bool IsNullOrWhiteSpace(this string str)
{
return string.IsNullOrWhiteSpace(str);
}

2种框架下实现的逻辑方式不同,为了只编写一套代码(该情况为一个方法),此时就需要使用预处理指令编写条件指令。

在库或应用中,使用预处理器指令编写条件代码,针对每个目标框架进行编译。关于预处理指令请参考《C# 预处理器指令》

使用预处理指令编写条件代码的实现方式如下:

///


/// 判断字符串是否为null、空或者空白
///

///

待判断的字符串
///
public static bool IsNullOrWhiteSpace(this string str)
{
#if NET35
return string.IsNullOrEmpty(str.Trim());
#else
return string.IsNullOrWhiteSpace(str);
#endif
}

///


/// 判断字符串是否为null、空或者空白
///

///

待判断的字符串
///
public static bool IsNullOrWhiteSpace(this string str)
{
#if NET35
return string.IsNullOrEmpty(str.Trim());
#else
return string.IsNullOrWhiteSpace(str);
#endif
}

上面的实现方式是在一个方法内进行条件区分,下面介绍在同一个类中(方法之外),使用条件区分不同逻辑的实现方式

#if NET35 || NET40 || NET45
///


/// 对URL字符串进行编码
///

注意:.NET Core 转义后字母为大写
///


///

有效的url字符串
///

编码,默认为 UTF8
///
public static string UrlEncode(this string url, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
return System.Web.HttpUtility.UrlEncode(url, encoding);
}
#else
///


/// 对URL字符串进行编码
///

注意:.NET Core 转义后字母为大写
///


///

有效的url字符串
///
public static string UrlEncode(this string url)
{
return WebUtility.UrlEncode(url);//转义后字母为大写
}
#endif

#if NET35 || NET40 || NET45
///


/// 对URL字符串进行编码
///

注意:.NET Core 转义后字母为大写
///


///

有效的url字符串
///

编码,默认为 UTF8
///
public static string UrlEncode(this string url, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
return System.Web.HttpUtility.UrlEncode(url, encoding);
}
#else
///


/// 对URL字符串进行编码
///

注意:.NET Core 转义后字母为大写
///


///

有效的url字符串
///
public static string UrlEncode(this string url)
{
return WebUtility.UrlEncode(url);//转义后字母为大写
}
#endif

上面两段代码中的预处理符号 NET35、NET40、NET45 是.NET目标框架中预定义的预处理符号。

使用 SDK 样式项目时,生成系统可识别预处理器符号,这些符号表示支持的目标框架版本表中所示的目标框架。 使用表示 .NET Standard、.NET Core 或 .NET 5 TFM 的符号时,请用下划线替换点和连字符,并将小写字母更改为大写字母(例如,netstandard1.4 的符号为 NETSTANDARD1_4)。

.NET 目标框架的预处理器符号的完整列表如下:


除此之外,开发者可以通过配置自定义常量的方式达到与.NET目标框架中预定义的预处理符号相同的功能。


TRACE;RELEASE


TRACE;RELEASE

上述代码片段通过  节点 定义了2个常量(多个常量之间使用分号分隔)TRACE 与 RELEASE。

在编写C#代码时能够自动智能感知到自定义的常量


上面是定义的统一的全局变量,也可以在每个条件编译分组中自定义常量



ZCN.NET.BIMFace.SDK.xml
binDebug
NET_FULL



ZCN.NET.BIMFace.SDK.xml
binDebug
NET_FULL

PropertyGroup,是包含一组用户定义的 Property 元素。 MSBuild 项目中使用的每个 Property 元素必须是 PropertyGroup 元素的子元素。其包含如下的子元素




 更加完整详细的信息请参考微软官方文档《PropertyGroup 元素 (MSBuild)》

技巧:高版本的 Visual Studio 足够智能,能针对不同的API及时给出提示,指出API适用于哪种版本的.NET。比如下图中使用的 fileStream.WriteAsync()方法是异步方法,只在.NET4.5及.NET Standard2.0中受支持,在.NET4.0中没有异步方法,只有对应的 fileStream.Write()同步方法。


将代码修改为如下格式即可实现多版本


 

 

所以在编写代码时,建议在项目文件.csproj文件中做如下配置:


netstandard2.0;net45;net40


netstandard2.0;net45;net40

也就是将 netstandard2.0 放置在第一个位置,因为在VS中编写代码时当前编辑器环境是针对放置在第一个位置的.NET版本。

以前的写法是在/Properties/AssemblyInfo.cs里通过[assembly: AssemblyVersion("2.3.*")]这样的形式生成,但是现在默认关闭这个功能了,如果我们直接指定9.8.*会警告错误,加上False即可

为什么默认关闭?请了解下Roslyn中的确定性构建

为什么默认关闭?请了解下Roslyn中的确定性构建

其它生成方式、汇编内部版本号后面两位的生成规则,请看使用Visual Studio时是否可以自动增加文件构建版本、Visual Studio 2017中的自动版本控制(.NET Core)、如何有一个自动递增版本号(Visual Studio)

其它生成方式、汇编内部版本号后面两位的生成规则,请看使用Visual Studio时是否可以自动增加文件构建版本、Visual Studio 2017中的自动版本控制(.NET Core)、如何有一个自动递增版本号(Visual Studio)

msbuildtasks也了解一下,如果要兼容以前的内部版本号生成规则,可自己动手

msbuildtasks也了解一下,如果要兼容以前的内部版本号生成规则,可自己动手


NuGet包相关

静态文件如何指定复制行为等,或许会发现安装NuGet之后希望能编辑的文件仅仅只是一个链接而已,如何让它包含在项目里面呢,请参考微软官方文档 NuGet ContentFiles揭秘,带回解决方案级包的讨论

PackageReference 方式作为包管理格式,安装时不支持执行install.ps1等powershell相关脚本,init.ps1在解决方案第一次安装时可用。vs2017中,已不支持此功能,NuGet 3 – 什么和为什么-Powershell安装和卸载脚本

关于nuget包安装的相关行为估计都可以通过msbuild属性或者任务来搞定,这一切都是可以通过命令行来执行的,方便跨平台使用吧

msbuildtasks也了解一下,可以代替ps1脚本完成想做的事



推荐阅读
  • 在Java开发中,保护代码安全是一个重要的课题。由于Java字节码容易被反编译,因此使用代码混淆工具如ProGuard变得尤为重要。本文将详细介绍如何使用ProGuard进行代码混淆,以及其基本原理和常见问题。 ... [详细]
  • 本文探讨了在Windows系统中运行Apache服务器时频繁出现崩溃的问题,并提供了多种可能的解决方案和建议。错误日志显示多个子进程因达到最大请求限制而退出。 ... [详细]
  • 二维码的实现与应用
    本文介绍了二维码的基本概念、分类及其优缺点,并详细描述了如何使用Java编程语言结合第三方库(如ZXing和qrcode.jar)来实现二维码的生成与解析。 ... [详细]
  • JUC并发编程——线程的基本方法使用
    目录一、线程名称设置和获取二、线程的sleep()三、线程的interrupt四、join()五、yield()六、wait(),notify(),notifyAll( ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • 长期从事ABAP开发工作的专业人士,在面对行业新趋势时,往往需要重新审视自己的发展方向。本文探讨了几位资深专家对ABAP未来走向的看法,以及开发者应如何调整技能以适应新的技术环境。 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 解决PHP项目在服务器无法抓取远程网页内容的问题
    本文探讨了在使用PHP进行后端开发时,遇到的一个常见问题:即在本地环境中能够正常通过CURL获取远程网页内容,但在服务器上却无法实现。我们将分析可能的原因并提供解决方案。 ... [详细]
  • PHP面试题精选及答案解析
    本文精选了新浪PHP笔试题及最新的PHP面试题,并提供了详细的答案解析,帮助求职者更好地准备PHP相关的面试。 ... [详细]
  • 本文详细介绍了如何调整 Kettle 的内存配置以优化性能,并指导用户如何正确设置日志输出中的时间类型,确保数据处理和监控的准确性。 ... [详细]
  • 在尝试启动Java应用服务器Tomcat时,遇到了org.apache.catalina.LifecycleException异常。本文详细记录了异常的具体表现形式,并提供了有效的解决方案。 ... [详细]
  • 本文详细探讨了在Java中如何将图像对象转换为文件和字节数组(Byte[])的技术。虽然网络上存在大量相关资料,但实际操作时仍需注意细节。本文通过使用JMSL 4.0库中的图表对象作为示例,提供了一种实用的方法。 ... [详细]
  • Maven + Spring + MyBatis + MySQL 环境搭建与实例解析
    本文详细介绍如何使用MySQL数据库进行环境搭建,包括创建数据库表并插入示例数据。随后,逐步指导如何配置Maven项目,整合Spring框架与MyBatis,实现高效的数据访问。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • 探讨低代码行业发展现状,分析其未能催生大型企业的原因,包括市场需求、技术局限及商业模型等方面。 ... [详细]
author-avatar
手机用户2502883723
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有