迁移到.NET Core上
04/11/2016
39 分钟可看完
本文内容
[原文发表时间]:February 10, 2016
迁移到 .NET Core上
.NET Core离RTM 发布版越来越近了。仅仅两个月之前,我们宣布了.NET Core 和 ASP.NET Core的RC 发布版。
作为我们验证的一部分,我们正在和内部还有外部客户一起把他们的代码移植到.NET Core上去。我们收到了很多要求都是关于你们应该怎样把已存在的代码合并到.NET Core,以及怎样继续面向.NET Framework工作。在本贴中,我想提供给你们一个大概的流程那就是以此把已存在代码移植到.NET Core上, 什么样的应用程序才是理想的,我们提供什么样的工具能帮助你移植你的程序,我们怎样把更多的API放到.NET Core中去帮助你们更好地用已存在的代码工作在.NET Core上。
我们很愿意和你谈谈!
你有把一个应用程序或者类库迁移到.NET Core上去吗?你有试着在.NET Core和其他平台,例如.NET 框架之间共享代码吗? 如果是这样,我很乐意同你谈谈你的经历,是否有什么事我们可以去做帮你把事情做得更好。如果你感兴趣,请在微软的网站上的immol上联系我,我会给你打电话。
你想迁移什么东西?
在你迁移任何资源到.NET Core之前,你应该知道你当前代码以什么为基础,即它的结构和外部依赖关系。想一想功能性要求,就是.NET Core必须提供,并且你想使之发挥作用。然后你可以反向地思考什么资源适合迁移,什么不应该被迁移,应该为.NET Core去写什么样的单独的代码。
让我们来看看.NET Core必须提供什么。RTM 版本的.NET Core 将会遵循以应用程序下模式:
· ASP.NET Core 应用程序和服务
· 通用的Windows 平台( UWP )应用程序
· 控制台应用程序
让我们快速看一下每一个模式然后了解下迁移在他们的语境下意味着什么。
ASP.NET Core 应用程序和服务
迁移的原因? 主要原因是迁移已存在的的程序使之可以跨平台运行。例如,这可以使得在Mac上运行OS X同时运行也可以把你的网站运行到Linux 上(我们非常乐意这能实现,但是这确实是你的选择)。但是如果你还停留在Windows操作系统上,你也许想要看一下ASP.NET Core,因为它提供了新的功能并且不需要广泛安装框架的机器,这就避免了一些问题,例如需要机器变化,部署过程中需要管理员权限,GAC政策等等。
好的移植选择?
ASP.NET 的基础是使用MVC 并且/或者 WebAPI的网站。
太适合移植吗? 如果你的大部分网页程序正在使用WebForms,移植到ASP.NET Core和重新实现是等价的,因为WebForms不被支持。然而,如果你认为使用这样的方法是一个在某种方式上重构你的程序的机会,那么这就不是一个瓶塞,因为MVC/WebAPI以更简单更现代的方式来编写web应用程序。
通用的Windows 平台( UWP )应用程序
迁移的原因? UWP统一了Windows设备系列, 范围从PC到平板装置,手机,还有Xbox。现在还包括无头式外设物联网(IoT)相关的设备。UWP 提供了很多非常棒的功能,例如一个应用程序商店允许你的应用程序更容易货币化。并且Windows运行时(WinRT)提供了丰富可用的完全本地化和强大的操作系统APIs,例如XAML和DirectX组件。
好的移植选择? 如果你正在面向Windows 8或者Windows Phone 8.1, 那么你已经准备做这些了:这些是.NET Core应用程序。 入果你是在维护Windows Phone Silverlight 应用程序,那么你已经非常接近了。实际上,任何Silverlight应用程序都是一个好的选择,因为那些API设置是以Silverlight上可用的API 为基础的:大部分API在.NET Core上可用,并且可以变为WinRT APIs, 在这种情况下,你会经常接触触摸式窗口,例如更改命名空间。实际上,有一个桥梁可以帮汉族我们做这个转换。
太适合移植吗? 丰富的桌面应用程序利用了Windows Forms 或者WPF 的优点,是非常合适移植的,因为两种技术都支持.NET Core。然而,WinRT 提供了一种本地化的基于XAML UI的技术,它和Silverlight, WPF非常相似。所以如果你打算重新设计你的程序使之可以跨越各种平台,那么这就不会成为你的一个阻碍。
控制台应用程序
迁移的原因? 你应该看看对于控制台应用程序使用.NET Core最重要的原因之一是它允许面向各种操作系统(Windows,OS X, 还有Linux)。另一个重要原因是控制台程序的.NET 本地化将最终会支持生产自足,单个文件使用最小的依赖项去执行。
好的移植选择? 几乎任何控制台应用程序都是公平的游戏,依靠你的依赖项。例如,如果你的控制台程序是用COM实现Windows 或者Office,那么也许迁移起来比较困难,也就是说,一个控制台程序解析一个CSV文件并且调用一个一个WCF服务。作为一个数据点,C#和VB编译器就是.NET Core控制台程序,即就是dotnet命令行工具集。
太适合移植吗? 没有一个标准的很适合迁移的例子;它是你的依赖项的一个功能。
.NET Core和.NET Framework的关系
在我们开始迁移之前,知道.NET Core和.NET Framework怎样联系是很有用的,尤其是可用的API.这样可以帮助你获得一张API是怎样的演化并且反过来能让你计划你的程序和类库的图片。
很多人认为.NET Core是.NET Framework的一个子集。你必须了解这个观点是错误的。正确的是:.NET Core在API这方面是更加简单化,我们计划并且将继续保持,.NET Core有它独有的API和技术。这包括工具式安装,例如不仅是.NET Native,而且包括类库。
然而,要知道庞大数量的.NET Core API和.NET Framework是共享的。这就使得我们可以非常容易的去写同时可以在.NET Core和.NET Framework上工作的类库。实际上如果你的目标不是.NET Framework 4或者Silverlight,那么所有的portable类库都是.NET Core类库。如果你想知道这是怎么被设计并且.NET Core是怎样简单地被portable类库写成的,那么可以看一下这篇博客。
当然,数量庞大的已知代码是面向.NET Framework的,把已存在的.NET Framework类库转换成.NET Core是非常有挑战性的,那么现在让我们花些时间来看看两者之间的主要不同之处吧。
.NET Core和.NET Framework之间的不同之处
两者之间的不同之处可以总结成以下三点:
1. 基于NuGet. .NET Core是分布式的一种NuGet包,允许本地部署。相反,.NET框架总是安装在全系统的位置。这种区别对类库来说没什么;但是对于那些要部署关闭依赖项的应用程序来说是很重要的。但是我们希望这个模型来更改怎样使类库的所有者可以利用新功能的优势。因为应用程序可以简单的部署到一个新版本的.NET Core上(不必像.NET Framework一样必须等着被广泛适用),所以只是比较少的那些类库的所有者会利用最新的功能的优点。
2. 好地层次性 .NET Core是以特殊方式设计成具有分层式的。目标就是创建一种可以容纳多种功能和系统约束条件并且不强制用户去重新编译他们的类库或者说产品新特性的.NET。那么这就意味着我们必须移除一定的API,因为他们是把低等级的组件绑定到了高等级的组件上。在这种情况下,我们会提供一种可替换的方法,那就是采用扩展的方法。
3. 疑难技术的免费 .NET Core不包括一定的技术,所以我们决定不继续因为我们发现他们是有问题的,例如AppDomain和沙盒技术。如果对.NET Core来说,这个方案是可以理解的,那么我们的计划将会有替代品。例如,对于加载并且分离程序集来说AssemblyLoadContext``替代AppDomains 。
第一个观点是指我们现在全然接受对于核心的开发经验来说,NuGet是第一等级的概念。我们相信这个一个自然的发展就像现在你们很多人已经开始使用NuGet去获得第三方的依赖项了。
第二和第三个观点是指当面向.NET Core时,一定数量的API并不可用。那么让我们来看下你应该注意哪些方面。
映射
随着.NET 本地化的到来,我们有了一种技术可以静态地连接你的应用程序在.NET 框架和第三方依赖项之间。要这个链接可行,识别出你没有正在使用的框架部分是非常重要的。在其他技术上,例如C++, 在某种程度上来说是很简单的,因为这些系统没有映射。当然,.NET本地化依然支持映射,但是我们想要这个平台发挥更好地收益,那就意味着你不需要为你没有使用的功能付费。这对于反射来说尤其真实,因为它规定了运行时和编译器可以基于静态信息。
所以最好的方式就是,映射在.NET Core中是一种可选择的组件,那你也许就一点也不想使用你的应用程序了。比较棘手的部分是System.Objec ``在映射上通过``Object.GetType()``有一个依赖项。为了打破这种依赖,我们决定System.Type ``不再代表全面反射类型信息,只保留类型名称。这就意味着``System.Type``不再包含诸如``GetMembers()这类的API,但是继续公开诸如Name这些API。
为了获得额外的类型信息,你必须要调用一个名叫GetTypeInfo()扩展方法,这个方法存在于System.Reflection``上。它会返回``Type``常用的类型信息。换句话说,就行下边的一行代码:
C#
var members = obj.GetType().GetMembers();
变为:
C#
using System.Reflection;
...
var members = obj.GetType().GetTypeInfo().GetMembers();
如果你有使用过.NET Core上的映射的经验,那么你可能已经注意到一定的API概念,例如BindingFlags也被移除了。基于您的反馈,我们最近决定把这些API再加进来。
不继续使用的.NET Core技术
.NET平台是一种非常成熟的技术,已经有15年的历史。我们已经在这个平台开发了一系列的技术。在过去的几年里,我们已经学到了很多关于这些技术的知识,诸如他们怎么被使用,创建的结构,以及有什么限制等总而言之,我们已经确定了一套我们不再提升现代.NET 程序的技术,因此,不会把他们放到.NET Core上去。
当然,我们并没有从.NET 框架上移除任何技术。如果你正在使用它们,那你可以不用改变任何东西。我们将会继续在.NET框架上支持这些技术。但是,我们鼓励你避免在你的新的应用程序上使用那些依赖项,因为这会使你在移植你的程序到.NET Core上时非常困难。并且我们也不会在那些方面增加新的功能,所以你最好不要使用首选的代替品。
让我们看一下其中的一些。我会解释他们为什么有问题,你应当使用什么去代替。更多的信息和不继续使用技术的列表,请读我们的迁移指南。
应用程序域
为什么不被继续使用了? AppDomains需要运行时的支持并且费用昂贵。在被CoreCLR执行的同时,在.NET本地化并不是可用的,并且我们也不打算增加这种功能。
我应该用什么代替? AppDomains是用作不同的目的。对于代码隔离来说,我们建议使用进程或者容器来代替。对于动态加载的程序集,建议使用新的AssemblyLoadContext ``类。
远程处理
为什么不被继续使用了? .NET远程处理的思想 – 透明的远程过程调用 – 已经被确定为问题性的体系结构。在外围场所,常被用于跨AppDomain交流。最重要的是,远程处理需要运行时的支持,并且是非常重量级的技术。
我应该用什么代替? 对于跨进程的通信来说,进程间的通信(IPC)应该被使用,例如管道或者内存映射文件。跨机器时,你应当使用基于解决方案的网络,最好是一个低开销纯文本的协议,例如HTTP.
二进制序列化
为什么不被继续使用了? 经过十年的服务,我们已经知道序列化是异常的复杂和强大的兼容性,已经成为支持它的类型的负担。因此,我们决定序列化应该是一种可以在最要的公共API上使用的协议。然而,二进制序列化需要直接的知识类型,因为它允许序列化图表,包括私有的状态。
我应该用什么代替? 选择满足你目的序列化技术,在格式化和占用空间方面。最受欢迎的选择包括数据协议序列化,XAML序列化,JSON.NET,还有protobuf-net.
沙盒技术
为什么不被继续使用了? 沙盒,依靠运行时或者框架限制一个托管的应用程序可以进入,被认为是一个无目标的.NET Core。沙盒应用程序和组件确实是很难搞定的,建议用户不要太依赖它。它也会使得执行起来更加复杂,并且影响并没使用沙盒技术的应用程序的性能。因此,我们不在.NET Core上提供沙盒功能。
我应该用什么代替? 使用提供安全边界的操作系统,例如运行进程的账户使用最小的权限。
考虑迁移
理所当然,只是因为有些东西在.NET Core上不可用并不意味着我们就停止使用。在大多数情况下,知识意味着我们并没有时间去调查移植是否有道理或者不认为它是有关.NET Core提供的应用程序模型。因此,我们在这方面非常乐意得到你的反馈。
那些API的一些将会在社区中有代替品。请在注释中让我们知道你正在使用哪些,并且用的高兴吗。这样我们就可以让类库的所有者确保这些都可以在.NET Core上正常工作。
你们一些人已经在GitHub上填写问题要求一些特殊的组件被迁移。我们当前正在关注这些:
· System.Data. 尽管基本层是.NET Core的一部分,例如提供者模型和SQL客户端,一些功能当前是不可用的,比如schema的支持和DataTable/DataSet。
· System.DirectoryServices. 现在在 .NET Core上并不支持与LDAP或者Active Directory的通信。
· System.Drawing. 尽管严格意义上讲,它是一个客户端API,但是许多开发人员使用服务端上的绘画API去提供缩略图或者水印。我们现在在.NET Core上不支持这些API.
· System.Transactions. 在ADO.NET 不支持事物处理的同时,也不支持分布式的事物处理,包括环境交易和登记的概念。
· System.Xml.Xsl and System.Xml.Schema. .NET Core支持XmlDocument以及Linq’s XDocument, 包括XPath。然而,当前并不支持XSD (XmlSchema) 或者 XSLT (XslTransform)。
· System.Net.Mail. 现在并不支持使用这些API从.NET Core发送邮件。
· System.IO.Ports. .NET Core现在并没有能力和串行端口进行通信。
· System.Workflow. Windows 工作流基础( WF )现在在 .NET Core上是不可用的。
· System.Xaml. 当创建UWP应用程序的时候,开发人员会使用WinRT XAML APIS, 因此,.NET Core现在不包括托管的XAML框架,其中包括解析XAML 文件的能力还有实例化被描述的对象图。
有关完整列表,请看标记为port-to-core的CoreFX问题。请注意这不代表着我们对所有这些组件的开源化的承偌,甚至把他们迁移到.NET中去 – 仅仅是这都是从社区的人员愿望出发而这样做。也就是说,如果你关注上边列表所列的任何组件,你可以考虑加入GitHub的讨论组,那么你的意见有可能被采取。如果你认为缺了什么,填写你的新问题。
你有兴趣帮助我们迁移一个组件吗?在很多情况下,.NET源代码的执行情况在MIT下已经是可行的了,作为参考代码的一部分。我们正在寻找方法使得社区可以帮助我们在迁移上有些成绩。如果你想参加,在microsoft.com上的immol给我发邮件。
并且,我们正在研究用户发现的在迁移时特别具有挑战性的方面。例如,我们已经有好几次会议关于最小化映射的不同。
知道你的代码有多方便迁移
在你尝试迁移之前,应该在你的进制文件上运行API端口。这将会产生一份可以提供两块有用信息的报告:
· 高级别总结。 这份总结给了你每一个程序集的百分比,可以告诉你的迁移的花费。可以表现出你的哪部分组件很难迁移,哪部分组件容易迁移。
· 不可迁移API列表。 它提供了一份表格,列出了不可迁移的可用API。同时也包括了建议可以改变的,调用替代品。
有的时候,这个高级别的总结可能会造成误导。要确保浏览一下不能迁移的API – 有时候,很多问题可以用这种方法修好。例如,许多映射API已经移走了,但是修理的方法很简单,就是插入一个调用函数到GetTypeInfo()。
我们非常鼓励你使用API移植。不仅这个工具可以很有效的帮助你,它也可以帮助帮助我们在迁移的时候确定API的优先次序,因为它会给我们把遥测数据发送回来。我们收到的数据仅仅是你在你的代码中调用的框架API的列表。那么通过这种方法,我们可以知道,哪些API经常被客户使用,这些API是要迁移到.NET Core上的。我们的目标是确定这些API的优先次序。别担心 – 我们不会收集你的代码的任何信息。你不必相信我的话:API 迁移是开源的并且被贴在GitHub上。
如果你想学习API Port是怎么工作的,你应该在Channel 9看一下关于API Port组的访谈:
迁移到.NET Core上去
现在你已经对这个功能提供的并且它与.NET 框架的不同有了很好的理解,那么让我们谈一下关于迁移的东西吧。
在你开始迁移之前,你应该知道你的应用程序结构,并且知道你想迁移那一部分。你大概可以用三种方法:
1. 协同进化. 在你想保留你的已有的.NET 框架资产时(例如桌面应用程序)而且也想利用.NET Core 程序模型,例如面向移动端设备的UWP。另一个通用的方法就是基于桌面应用程序的.NET 框架和基于服务的ASP.NET之间通信。在这两种情况下,目的就是分享一些.NET Core和.NET 框架的功能。
2. 迁移。 在这种情况下,你有一个已有的应用程序,例如一个ASP.NET 4 MVC 应用程序,你想把这个程序全部移植到ASP.NET Core上去。因此,目的不是把.NET 框架和.NET Core作为目标,二是可以快速的适应你的代码以便于它可以为.NET Core成功编译。
3. 从零开始。 在这种情况下,你不必关心已存在的程序因为你要在.NET Core上从零开始一个程序。但是最终你可能喜欢用到一些样例或者说纳入.NET 框架的一些代码片段。所以你依然需要知道怎样使这些代码能在.NET Core上工作。
我将会集中注意力到第一种和第二种方法上因为第三种只是别人所讨论的技术。
一般方法
永远要记住,每个代码库都是不同的。因此,代码迁移的过程会发生变化并且很大程度上依赖你代码库的状态,所以我不能提供给你一个可覆盖所有情况的方法。下面所列的方法和技术可能不会按照期待的工作。你必须在这种情况乱下适应他们。
下面是迁移时一些简单的方法
1. 标识你想迁移到.NET Core上的工程。
2. 知道这些工程存在外部依赖项并且确保他们既和.NET Core兼容,有等效替代,或者可以被分解。
3. 使得那些工程面向.NET 4.6.1的框架。这使得你可以使用API备选方案,即就是我们针对这些方案所介绍的.NET Core不支持哪些已存在的APIs。记得要更新所有的消费的工程,否则由于.NET 框架不一致的问题,在编译时会出现错误。
4. 重新编译
5. 运行API Port
6. 更改你的代码以便处理API Port的问题
重复4-6步知道所有的API Port问题被处理。然后创建新的.NET Core工程并且把你的代码放进去。如果没有很多API Port不能发现的问题,那么你的代码应该能编译成功
协同进化的.NET Core和.NET 框架应用程序
在这种情况下你想在已有的.NET 框架程序和即将被创建的.NET Core程序之间分享你的代码的话。有两种方法你可以采用:
· 共享资源。 共享工程允许你链接工程级文件:当引用一个共享的工程时,所有的资产文件都变成这个工程的一部分。这个允许你使用条件编译和局部类使你的代码适应其他平台。
· 共享进制文件。 你可以把你想共享的代码拆成块,放进一个可迁移的类库,这个类库可以被.NET 框架和.NET Core应用程序引用。
在你想通过使用#if来是适应你的代码的情况下,共享资源是非常简单的。如果你想确保你不会创建很乱的代码通过使用#if条件句。试着集中处理尽可能多的不同代码。另一个比较好的窍门是使用分部类其中的一个类的一部分是在共享的项目而另一部分是由平台待定的项目。
取决于你们组合应用程序的大小,使用共享进制文件也许是一个更好并且更合理的方案。原因就是类库正在创建可以被部署并且作为鼓励测试模块的的单元。在源代码中分层也是明显可行的,但是需要更多的约束条件,因为它是基于一定的公约。
为了共享这些文件,你要创建完全可以在.NET 框架和.NET Core之间移动的类库。为了实现这个,你必须标识你想共享的组件。我建议你最好先确定你想迁移的组件和你不想迁移的组件不在同一个工程中。换句话说,一个工程要么完全可以共享,要么就不能共享。一旦这个确定了,那么就开始创建迁移类库并且移动代码。
作为一个经验法则,我会说共享二进制文件非常适合您的业务逻辑和核心层,同时对于在目标平台上可以高效部署的组件来说,共享资源是非常合适的,例如,你需要与UI API交互或者调用操作系统的特殊组件。值得指出的是.NET Core和.NET 框架的交叉口都是非常巨大的。实际上,几乎所有的.NET Core提供的API在.NET 框架上都是可用的,第二种方法并不像说的那么多限制。
关于这两种方法的详细比较,请看博客。
把代码迁移到.NET Core
为了使得迁移更容易,我建议你在迁移的过程中把.NET 框架作为目标的时间尽可能放长点,使用API Port去标识迁移遇到的问题,大部分问题解决时才使用这个大的转换。用这种方式,你就不会在代码库并没有建立很长世间的担忧了。
当在计划你的迁移工作的时候,你应该也要考虑一下测试并且把测试当作你产品迁移的一部分。迁移往往导致很大的代码变化,所以如果你要确保中途没有引进新的bug。
需要最多的工作区可能还需要应用程序模型,无论是桌面应用程序或者ASP.NET:
· 对于桌面应用程序来说,很值得使用协同进化的方法因为它避免不具有任何临时版本的应用程序。
· 对于APP.NET应用程序来说,好方法是去编译这个应用程序,当作.NET 框架程序。第一个步骤,用ASP.NET Core代替ASP.NET 4。你可以决定舍弃你的程序,然后从新的.NET Core程序模型上获益。或者,你想全部移到.NET Core上去,运行API Port并且指出问题。一旦这两个完成了,你就可以把整个程序放到.NET Core上去了。
但是如果你的程序比较小,也许可以使用大铁锤方法:简单地创建新的工程,复制&黏贴已有的代码并且修复所有的编译错误。
更多的细节?
对于更多的细节,请看以下这些资源:
.NET Core发展
API Port
总结
在这篇博客中,我概述了.NET Core带来的好处,还有就是哪类应用程序和组件可以从它获益。我们也在关注可以平衡.NET Core和已有程序的方法,既可以通过增加.NET Core基础经验到你的文件夹中或者在.NET Core顶端完全移植你的程序。我已经展示了你可以怎样使用API Port并且帮助你了解你的组件还有它们可迁移的程度。
如果你有任何问题或者关注,请留言让我们知道。如果你有把代码迁移到.NET Core上的经验,并且愿意谈谈这些,我非常高兴在Microsoft.com的immol上收到你的来信。我非常想知道我们可以做什么帮助你提高你的迁移经验。
祝你迁移快乐!