ASP.NET Core 削减对.NET Framework 的支持

  • Jonathan Allen
  • Jeff Martin
  • 大愚若智

2017 年 5 月 14 日

话题:.NET语言 & 开发架构

上周五,ASP.NET Core 悄然进行了调整,由原本同时支持.NET Standard 1.和.NET 4.改为仅支持.NET Core 2.0。这意味着在 Mono 或完整版.NET Framework 基础上运行的 ASP.NET Core 1.0/1.1 应用程序将无法升级至 ASP.NET Core 2.0,这一新版本预计会在未来 2-3 个月内发布。由于未经公开讨论或正式说明就对平台做出如此重大的改变,此举惹怒了很多重量级开发者

跨框架支持曾被视作 ASP.NET Core 的一个重要特征。其基本思路在于,用户可以在立即切换至 ASP.NET Core 的同时继续使用自己原有的服务类、代码库,以及第三方库。随后等到.NET Core 发展到与.NET Framework 保持一致后,还可以随时切换回来。

ASP.NET 开发经理 Eilon Lipton 解释了此番改动背后的原因:

没错,对于 ASP.NET Core 2 而言,大部分库主要是以.NET Core 2 为目标的,这是为了能顺利地使用.NET Standard TFM 中暂不可用的新增 API。对于需要以诸如 Microsoft.Extensions.*、Entity Framework Core,以及其他少数库等多个平台为目标的代码,需要继续使用.NET Standard。

在询问微软为何不直接开发包含必要 API 的.NET Standard 2.1 后,Kévin Chalet 继续问到:

我的问题很简单:RTM 之前,你们是否由可能重新调整 / 撤销这些改动,让用户能够继续在.NET Desktop 上使用 ASP.NET Core 2.0 软件包,还是说.NET Desktop 上的 ASP.NET Core 2.0 已经彻底没戏了?(这对包括我在内的很多人都是个很不利的因素)。

虽然不打算全文引用,但很多开发者均对此表达了类似的顾虑。

Scott Hanselman 试图通过自己对于在.NET Framework 中继续使用 ASP.NET Core 的主要理由安抚开发者:

AD – 老实说,如果希望直接调用 LDAP,这方面还存在比较大的问题。虽然目前依然可以通过 Windows Auth 进行身份验证,但我们计划今年夏天针对 Core 2.0 提供更具体的 DirectoryServices 名称空间。

Drawing – 这也是个问题。我们计划今年夏天为 Core 2.0 提供相关能力,但在此之前,这些 Netstandard 选项也广泛存在于 ImageSharp、ImageResizer、Mono 等选项中。

COM 自动化 – 这一特性在 Core 2.0 中从未实现过,如果希望自寻烦恼,开发者当然可以使用 PInvoke,如果这样的烦恼还嫌不够,还可以为 net461+ 进程使用本地 WebAPI。

与 WFP 应用实现代码共享 – 没错,这在 Netstandard2.0 中是完全可行的。

随后 Scott Hanselman 重申说:

.NET Core 正在经历快速的并行发展,发展速度远远超过了完整版.NET Framework,其实这是好事。通过基于.NET Core 2.0(别忘了,这可是.NET Standard 的超集)构建 ASP.NET Core 2.0 应用,这意味着可以用远超 NetFx 甚至 Netstandard 的速度进行开发。

虽然.NET Core 2.0 是.NET Standard 的超集,但并不是.NET Framework 的超集。Kévin Chalet 写到:

很多被重新引入.NET Core 2.0 的“古老的”API 实际上都是历史残余,(从功能上来看)从来未能正常使用过。更不用提.NET Core 目前依然缺乏很多 API,甚至特意省略了对很多领域的支持(IdentityModel、WCF 服务器、远程、完整的 AppDomain 支持等)。

试图劝说大家可以“安全地”将自己使用.NET Desktop 开发的 ASP.NET Core 1.0 应用迁移至使用.NET Core 开发的 ASP.NET Core 2.0 应用,在我看来这是彻头彻尾的谎言。

谈到该技术的发展速度,他又补充说:

我觉得(大部分)人们关心的并不是这个。很多人根本不需要发展速度如此快的库,他们更想要稳定的库,借此按自己的节奏将其集成于老应用,同时无需担心兼容性风险。

包括 Allan Lindqvist 在内,很多开发者还担心此举可能造成社区产生嫌隙。

很多真心认可 Asp.NET Core 1.1 的开发者感觉自己被背叛了,他们现在可能会不愿意切换至没有了 Core 的 2.0 版。这么说也许显得不合理,但我想说的是,很多人都会产生这样的感觉,同时他们将不得不应对其他更保守的同事提出的各种问题。

这些重要吗?Asp.NET Core2/.NET Core2 会因此而“亡”吗?不会,但会拖累用户的接受速度,让整个生态系统支离破碎,在我看来这才是最大的悲哀。我希望大家都能使用 2.0 版所包含的所有出色技术,但是将交叉编译能力限制为只能用于.NET Standard 无疑会对此产生影响。当然,对大部分固守原有代码库的普通开发者来说这不算是个问题,但只不过是将问题转嫁给了他们的经理。

另一个顾虑在于,这个决定是内部私下做出的,并没有公示或公开讨论。Demis Bellot 认为:

由于官方事先并未公布这一消息或听取用户反馈,也没有进行意见征集或给出合理的理由,这个决定“似乎”是完全与社区无关的,也没有对现有.NET 生态可能产生的影像进行任何分析,可能导致整个生态系统在未来几年内变得支离破碎。取消对.NET v4.x 的支持,意味着很多企业无法通过足够平滑的方式将现有代码迁移至 ASP.NET 的全新开发模型。这一举措不能让他们加速接受.NET Core,反而会打消他们这样做的念头,严重的碎片化会进一步导致整个.NET 生态一分为二。我觉得最终的结果将会与 Python 2/3 的情况差不多,不可避免得受到碎片化的伤害,造成十几年都无法挽回的后果。

继续说回有关库支持的问题,Gulbanana 列举了一些他本人需要,但.NET Core 不支持的特性:

我们是 Core CLR 和可移植能力的忠实“铁粉”,我们已经将自己的很多组件移植到了多个不同的 Netstandard。目前我们已经通过 Linux 托管了一个应用,预计未来会有更多应用这样做。然而很快将完全无法进行移植了!目前我们一些依然在维护的代码依赖下列技术:

NTLM 身份验证(包括一些与令牌和 SPN 有关的高级应用场景)。

System.Drawing如上所述(这方面有个趣闻:我们需要使用一些古老的图像格式,例如 TIFF,而新的 OSS 库根本不支持)。

连接至WCF/SOAP API - 依然在我们的代码中大量使用。

Windows Crypto API(包括 RSACng、ECDSA,以及用于 PKI 令牌的 CSP)。

WPF GUI - 我们的客户短期内不会升级至 Windows 10,但就算升级了,他们也不会使用商店应用。

Microsoft Office Interop实现数据导入和导出(主要使用 Access 和 Excel)。

Visual Studio 的扩展能力

第三方插件,例如Oracle ADO.NET提供程序

Core CLR 可能永远不会支持其中的某些技术,但这些技术也有自己的价值,同时也是新应用开发工作的基础。我们需要一种能支持这些技术的.NET 版本。目前我们的立场还是可控的:我们会隔离“遗留”(实际上也就是依赖具体平台的)依赖项,将其隔离在符合 net461 规范的专有组件内。如果某个业务应用依赖这些功能,则可以针对 Netfx 建立依赖项。虽然我设想以后的应用很少需要这样做,但我不觉得可以彻底避免这种做法。

有关 csproj 和 SDK 的这一切做法都是因为互操作实在太重要了。ASP.NET Core 是大家希望实现互操作的诱因,我觉得取消这一点是不合理的。当然,“虽然可以单向加载引用,但最终很可能在运行时失败”这种做法绝对算不得互操作。

Tim Miller 进一步补充说:

我们目前的 Web 应用中还用到了ODataSignalR,在可以升级前必须继续使用 ASP.NET Core。然而我最不希望遇到的情况就是,由于对 ASP.NET Core 1.x 的支持被削弱而导致我们陷于其中,并且因为目前使用的框架部件不被支持,我们还无法进行迁移。我认为 ASP.NET Core 最终将全面取代 ASP.NET,也许前路还很长,但终究有一天会实现的。因此我很不希望现在用 ASP.NET MVC 5 重写我们的 UI(少量内容虽已重写,但主要是因为规模小,所以过程简单而已),而仅仅几年后当 ASP.NET 寿终正寝后又使用 ASP.NET Core 再次重写。

大家关注的另一个重点是Entity Framework。虽然有 EF Core,但很少有开发者会用它而不使用 EF 6,因为 EF Core 仅支持老版本所具备的部分功能。实际上 Reddit 等论坛上对此有一种普遍意见:ASP.NET Core 应当用于新的项目,但在变得更稳定之前,开发者应避免使用.NET Core 和 EF Core。

对 Louis Lewis 来说,最大的障碍在于硬件的检测:

从你的这个清单来看,我们目前正在使用但尚未移植的就是system.management DLL 了。具体来说,我们需要使用硬件标识符,这是为了在 Azure 服务总线和我们的全部许可服务器和客户端程序包之间自动建立链接。我不禁在想,这些东西已经不再是 API 了,是否会对我们造成某些严重的问题?

Christian Weiss 补充说:

当然了,Azure Service Fabric库也不支持.NET Core,对我们来说这也是个障碍。

修正方案

为了回应所有这些批评,Damian Edwards 已经公布了一个修正方案。

在.NET Framework 上对 ASP.NET Core 1.x 的支持将至少再延长一年(直到 2019 年 7 月)。我们每年还会再次考虑这一时限,并保证会在 ASP.NET Core 1.x 支持终止前 12 个月发布通知(也就是说,我们会在 2018 年 7 月宣布是否继续延长一年至 2020 年 7 月)。(题外话:现在就考虑 2020 年的事情,有没有人被吓着?没有?好吧,那看来只有我了)。

新发布的 SignalR 将以 ASP.NET Core 1.x 为目标并为其提供完整支持(今年底发布)。

我们将继续更新 ASP.NET Core 1.x,以便支持新的语言版本或其他特性,这一做法与 System.Web 的处理方式无异。

运行于.NET Core 1.x 的 ASP.NET Core 1.x,其支持截止期限为 2018 年 7 月(无变化)。

运行于.NET Core 2+ 的 ASP.NET Core 1.x,其支持期限与运行于.NET Framework 的 ASP.NET Core 1.x 维持一致。这是为了确保受支持的 ASP.NET Core 与受支持的.NET Core 之间始终有相互重叠的部分,同时还支持运行于.NET Framework,这样客户就可以陆续将自己的代码移植到可支持的技术中。

.NET Core 1.x(作为运行时)的支持无变化,将于 2018 年 7 月结束。使用 ASP.NET Core 1.x 的客户需要在那之前迁移至.NET Core 2.0 或.NET Framework(或迁移至运行于.NET Core 2.0 的 ASP.NET Core 2.0)。

今年我们会将 System.DirectoryServices 和 System.Drawing 移植至.NET Core(完整支持、跨平台,今年夏季至少提供 Windows 预览版)。

在这之后还要将 ServiceBase 移植至.NET Core(用于支持.NET Core Windows Services)。目前没有具体时间安排,不过已经确定了这是后续要做的工作(随着工作的进展,时间安排无疑才能更精确)。我们还会根据客户反馈调整后续工作安排。

目前我们并无将 System.ServiceModel(服务器端 WCF)移植至.NET Core 的计划。但可能会进一步完善 WCF for .NET Core,并根据用户反馈添加新的功能,例如 HTTP 消息加密。

如果能早点公布这些信息,也许大家的抱怨会少一些,David Fowler 已经提到了微软希望从.NET Standard 中抽身的原因。

我们已经确认会将“字符串”作为.NET Core 需要继续完善的主要目标,大家提出了五花八门的想法,其中之一提到默认使用 UTF8 格式的字符串(兼容性问题还有很多,且听我一一道来)。

我们打算解决的另一个问题是:能够使用任何连续内存创建更低开销的数组 / 字符串切片(Slice)。我们已经增添了 Span,并在努力实现对应的 Buffer。这也许意味着 String 和 Array 所实现的新接口使得我们可以创建完全无需分配的切片。

这就为 String 提供了新方法,可在无需每次都分配数组的情况下进行拆分。

但这会对 Int、UInt 等(TryParse)接受 Span 和 Span 的内容造成负担。

这还会产生可以接受 Span 的全新编码例程。

Buffer 和 Span 可以让我们用统一的方式呈现内存,我们还希望对网络栈进行升级,借此传递可以接受 Span 或 Buffer 的 Pre-pinned buffer。

Kestrel 将在 2.x 版本期间(目前目标为 2.1 版)实现 HTTP2,这就要求需要通过 SSL 流的新 API 来处理ALPN

运行于.NET Core 的 HttpClient 支持双工流(Duplex streaming),借此将可以通过 SignalR 以 HTTP 的方式用一个非 WebScoket 的 HTTP 请求实现流端点。

我们还通过 HttpClient 和System.NET.Http为头解析器的实现创建了分支,并更改了名称,这样就可以改进使其同时支持.NET Framework 和.NET Core。.NET Core 已经包含这些改进的副本,但并未投入使用,因为其实并不需要改进(未获使用)。

与线程有关的很多新原语(Primitive)需要新的 API,借此可以塑造全新的适用场景,不过这些只是我的个人看法。

虽然这些原因可以安抚一些人,但 Stefan Olson 也归纳了大部分开发者的想法:

在我看来,似乎 ASP.NET Core 将运行于.NET Core 而非.NET Standard?那么我依然纳闷.NET Standard 价值何在?是否在诞生的那一天就注定了它最终会被抛弃?

我觉得更合理的做法是,ASP.NET Core 需要依赖.NET Standard 2.1,因为该框架目前还不具备用户需要的某些功能。那么我们只能继续等待完整的.NET Framework 支持这些额外的功能,随后才能使用最新版 ASP.NET Core。这样是可行并且合理的。

但他们竟然抛弃了 Standard,使其完全发挥不出作用(不知道我对目前这种混乱的局面理解是否正确)。

目前.NET Standard 面临的局面还不是那么悲催,它依然是一种向第三方库同时提供.NET Framework 和.NET Core 能力的好方法。

挫败感还在继续

Stack Overflow 的 Nick Craver 通过实例论证了很多团队可能产生的沮丧。在花了大量时间将他们的代码移植到 ASP.NET Core + .NET Framework 之后,他们觉得自己的努力做了无用功:

基本功能方面依然存在差距。移植到 1.x 的工作基本上完全就是一种旁侧(Lateral)项目,可供我们使用的功能极为有限。2.x 版在某种程度上来说还算值得进行升级,但我们也无法保证最终一定就会这么做。有很大可能我们花费大量时间、成本以及精力移植到 1.x 版的努力最终会搁浅,因为随后还需要依赖完整的 Framework。我不希望自己公司在这方面打赌碰运气。最终我们能得到的支持可能远远比不上目前使用的 ASP.NET 4.x。

除非这一点能有所改变,否则我们的应用不会移植到 ASP.NET Core,而我们所有的库可能会面临比缺乏内部测试更糟糕的麻烦。

真心希望你们能重新考虑这个决定。希望有朝一日可以让 Stack Overflow 充分利用我们贡献出来的数千小时个人时间,帮助.NET Core 平台更上一层楼。

Quentin 对此表示了赞同:

没错,具体原因我很理解,真希望我能说“早就猜到有这样的一天”,但不事先通知就放弃,这一点真的让人很不爽。

和其他厂商类似,我们也有应用生态,有诞生于十几年前甚至更早之前的库,有很多 MVC 3、4、5 应用,有控制台应用,有 Windows 服务,有成百上千行代码。相比其他一些技术,情况完全没有糟糕到必须要进行升级的地步。

最近我们使用 ASP.NET Core 1.1 开发了几个新的 Web 应用,这就必然要以完整的 4.6.2 版 Framework 为目标。这些应用大量运用了我们现有的共享库。我们已经将大量开发工作时间用于处理 project.json,随后是新出现的.csproj,这么做只是为了在同一个解决方案中混合使用新老.csproj,回退至 VS15 将只能使用古老的.csproj 类型,破碎的 System.Dependencies,冗烦的程序集绑定问题,构建和发布问题……等等等等。

而现在我们已经展翅高飞进入到一个死胡同里。是否就这样放弃数个人月的开发工作,或投入更多时间来查明哪些代码可用哪些又不可用,然后想方设法进行升级,接下来才能知道这样的做法到底可不可行。

不能说我完全不赞同目前的方向和必要性,只不过希望能提前通知一下大家。

秘密的意图

这一系列意见的关键在于,微软对这次的改动已经“蓄谋已久”了,只不过他们没有公开而已。David Fowler 认为:

这也是 1.1 版 ASP.NET Core 的生命周期被延长(虽然还是会结束)的主要原因。我们需要确保 SignalR 可以正常使用,因为那是我们选择使用 ASP.NET Core 2.0 的重要原因。于此同时,归根结底这并不是 ASP.NET Core 的目标,它们的目的始终在于作为统一的.NET Core 平台的一个组成部分发布给用户。

对此 Scott Sauber 回应说:

我是真正见证了每一版 ASP.NET 的发展过程(包括 Hanselman 和 Glenn 对 Docker 进行的 3 小时排错……因为我就是个技术宅),我会阅读博客,我随时刷 Twitter,我每年参加两次以上的技术会议,我会参加用户组,我会参与各种活动,但我从未见过有人针对 ASP.NET Core 的发展规划进行过此类讨论。相反,我只听到每个人都在说“它可以跨平台运行”,没错,感觉就像你想坐下时有人把椅子挪走了。我甚至做过有关 ASP.NET Core 的演讲,并且自己也说过“它可以在每个平台上运行”这样的话,现在感觉自己打了自己的脸。我认为,像是 ITT 那些游走在技术前沿的人也许能接受这样的消息,而这肯定会招致不少抵制。感觉当更多无法快速适应的开发者以及他们的经理,也就是比 ITT 更为保守的那群人听说了这个消息后,情况可能会变得更糟糕。

有关.NET Framework 的其他顾虑

这个讨论还谈到了.NET Framework。David Fowler 认为:

Span 本身有一个可以运行在各种平台的可移植版本,但我不确定.NET Framework 上的实现是否是其中速度最快的。这些 API 即将加入.NET Framework,它们可以充分利用 Span,但可能会比 Span 本身慢一些,毕竟范围非常大。我们上一次在.NET Framework 中更改字符串和数组又是什么时候呢?

问题不在于 Span 本身,而在于.NET Framework 造成的影响,以及它和其他有关.NET Core 的改进是否能够顺利实现。Matt Nischan 认为:

我们一直认为,要在可行的情况下尽可能使用各种新颖的库(例如 Kestrel),但依然要继续等待.NET Core 变得更稳定(很高兴我们耐心等待了,而原因仅仅在于这工具自身的变化),或者等待有更酷的 Core 版本最终融入到 Framework 中。我认为大部分人会假设,Corefx 的各种改进,就算不是全部,只是大部分被最终纳入 Framework vNext 或 vNext+1,那结果也是极好的。然而(从超过 3 万的浏览量)看起来,围绕 Framework 的各种改变并不能塑造一个面向未来的平台。

Frans Bouma 补充说:

确实是个好问题:关于将.NET Core 的功能向后移植到完整版.NET 的计划,到底还剩下些什么?看到这里我发现,完整版.NET 真是太脆弱了,代码的移植需要投入大量时间(它的改进实在是太慢),甚至可能随时中断(有这种可能,只是不知道具体会有多棘手),因此我认为这种情况应该不会经常发生。

这就可能产生两个结果:或者向后移植到完整版.NET 是可行的,只不过发布频率没那么高(例如每六个月或者每年);或者整个过程非常复杂容易出错。如果是前一种情况,只要按照步调进行就好,不过这不能让用户买单,并且别再使用后者作为借口。如果是后一种情况,那么说 Netstandard 是“异端邪说”就是最合理的结论了,毕竟未来最重要的还是.NET Core 的 API 不是吗?

阅读英文原文ASP.NET Core Drops Support for .NET

.NET语言 & 开发架构