一、ASP.NET Core 2 有什么新内容?

在 ASP.NET Core 框架的第 2 版中,有很多东西已经改变了。它的一些支持技术也有很多改进。现在是一个尝试它的好时机,因为它的代码已经稳定下来了,变化的速度也稍微稳定下来了。

最初的候选版本和 ASP.NET Core 版本 1 之间有显著的差异,版本 1 和版本 2 之间有进一步的变更。其中一些变化一直存在争议,尤其是与工具相关的变化;但是,的范围.NET Core 已经大规模增长,这是一件好事。

版本 1 和版本 2 之间引人注目的区别之一是从基于新的 JavaScript 对象符号 ( JSON )的项目格式更改为基于可扩展标记语言 ( XML )的csproj格式。但是,与原始版本中使用的格式相比,它是一个简化和精简的版本.NET 框架。

不同部门之间已经朝着标准化的方向发展.NET 框架,以及。因此,NET Core 2 有一个更大的 API 表面。接口规范,称为.NET 标准 2,涵盖了.NET Core.NET 框架和 Xamarin。还努力将可扩展应用标记语言 ( XAML )标准化为 XAML 标准,该标准将在通用视窗平台 ( UWP )和 Xamarin 上运行。表单应用。

C#和.NET 可以在各种各样的平台上使用,也可以在大量不同的用例中使用,从服务器端的 web 应用到移动应用甚至游戏(使用 Unity 3D 等游戏引擎)。在这本书里,我们将集中讨论网络应用编程,特别是让网络应用运行良好的一般方法。这意味着我们还将讨论使用 JavaScript 编写客户端 web 浏览器脚本以及所涉及的性能问题。

这本书不仅仅是关于 C#和 ASP.NET 的。它采用整体的方法来表现,旨在教育你一系列相关的话题。我们没有足够的空间深入研究每一件事,所以这里的想法是帮助你发现一些有用的工具、技术和技巧。

在本章中,我们将介绍两者的版本 1 和版本 2 之间的变化。网芯和 ASP.NET 芯。我们还将研究 C#语言的一些新特性。也有许多有用的增加和过多的性能改进。

在本章中,我们将涵盖以下主题:

  • 最新消息。网络核心 2.0
  • ASP.NET Core 2.0 有什么新功能
  • 性能提升
  • 。网络标准 2.0
  • 新的 C# 6.0 特性
  • 新的 C# 7.0 特性
  • JavaScript 注意事项

核心 2 有什么新功能

Core 系列主要有两种产品。首先是.NET Core,这是一个提供基本库的低级框架。它可以用来编写控制台应用,也是更高级别的应用框架的基础。

第二个是 ASP.NET Core,这是一个构建运行在服务器上的网络应用和服务客户端(通常是网络浏览器)的框架。这原本是的唯一工作量.NET Core,直到它的范围扩大到可以处理更多样的场景。

我们将分别介绍这些框架在较新版本中的差异。的变化.NET Core 也将适用于 ASP.NET Core,除非您在.NET 框架,第 4 版。

最新消息.NET Core 2

的主要焦点.NET Core 2 是范围的巨大增加。包含的 API 数量是原来的两倍多,并且它支持.NET 标准 2(将在本章后面介绍)。也可以参考。无需重新编译的. NET Framework 程序集。只要程序集只使用已经在中实现的 API,这应该就可以了.NET Core。

这意味着将有更多的 NuGet 包可以使用.NET Core。在以前的版本中,找到您最喜欢的库是否得到支持一直是一个挑战。作者建立了一个列出包兼容性的存储库来帮助解决这个问题。你可以在【https://anclafs.com/】https://github.com/jpsingleton/ANCLAFS找到ASP.NET Core 库和框架支持 ( ANCLAFS )列表。如果您想进行更改,请发送拉取请求。希望在未来,所有的包都支持 Core,这个列表将不再需要。

现在有了支持.NET Core,以及更多的 Linux 发行版。您还可以使用 Visual Studio 2017(仅企业版)执行实时单元测试,这与旧的 NCrunch 扩展非常相似。我们将在第 3 章设置您的环境中更多地讨论工具,我们也将涵盖集装箱化。

性能提升

一些更有趣的变化.NET Core 2.0 是对原始版本的性能改进.NET 框架。对许多框架数据结构的实现进行了调整。一些快速改进或减少内存的类和方法包括:

  • List<T>
  • Queue<T>
  • SortedSet<T>
  • ConcurrentQueue<T>
  • Lazy<T>
  • Enumerable.Concat()
  • Enumerable.OrderBy()
  • Enumerable.ToList()
  • Enumerable.ToArray()
  • DeflateStream
  • SHA256
  • BigInteger
  • BinaryFormatter
  • Regex
  • WebUtility.UrlDecode()
  • Encoding.UTF8.GetBytes()
  • Enum.Parse()
  • DateTime.ToString()
  • String.IndexOf()
  • String.StartsWith()
  • FileStream
  • Socket
  • NetworkStream
  • SslStream
  • ThreadPool
  • SpinLock

我们这里不讨论具体的基准测试,因为基准测试很难,您看到的改进显然取决于您的使用情况。需要指出的是,已经做了大量工作来提高的性能.NET Core。这些变化中有许多来自社区,这显示了开源开发的好处之一。这些进步中的一些可能会回到常规的未来版本.NET 框架。

对的实时编译器进行了改进.NET Core 2。仅举一个例子,finally块现在几乎和根本不使用异常处理一样高效,这在没有抛出异常的正常情况下是有益的。你现在没有借口不自由使用tryusing块,例如通过checked算法来避免整数溢出。

ASP.NET Core 2 有什么新内容

ASP.NET Core 2 利用了的所有改进.NET Core 2,如果你选择运行它的话。它也将继续运行.NET Framework 4.7,但最好在其上运行.NET Core 如果可以的话。随着范围和支持的增加.NET Core 2,这个问题应该比以前少了。

.NET Core 2 包含了一个新的元包,所以你只需要引用一个 NuGet 项就可以得到所有的东西。但是,它仍然是由单独的包组成的,如果你想挑选的话。他们还没有回到拥有一个大型集会的糟糕的旧时代。一个新的包调整特性确保如果您不使用包,那么它的二进制文件将不会包含在您的部署中,即使您使用元包来引用它。

设置 web 主机配置还有一个合理的默认值。您不再需要单独添加日志记录、红隼和 IIS。日志记录也变得更简单了,因为它是内置的,所以你从一开始就没有借口不使用它。

一个新特性是支持无控制器的剃刀页面。这正是它听起来的样子,它允许你只用一个 Razor 模板来写页面。它类似于网页产品,不要与网页表单混淆。有人说网络表单正在卷土重来;如果发生了这种情况,那么很有希望的是,抽象会被更多地考虑,它不会携带太多的状态。

有一种新的身份验证模型可以更好地利用依赖注入。ASP.NET Core 身份允许您使用 OpenID 和 OAuth 2,并为您的 API 获取访问令牌。您可能还想研究提供许多类似功能的 Identity Server 4 项目。

一个很好的节省时间的方法是,您不再需要在表单中发出具有属性的防伪标记(以防止跨站点请求伪造)来在 post 方法上验证它们。这一切都是自动为您完成的,这应该可以防止您忘记这样做而留下安全漏洞。

性能提升

ASP.NET Core 区的绩效有了额外的提高,但与中的改进无关.NET Core,这也有帮助。通过发送已经通过及时编译过程的二进制文件,缩短了启动时间。

虽然输出缓存在 ASP.NET Core 2 中不是一个新特性,但是现在已经可以使用了。在 1.0 中,只包括响应缓存,它只是设置正确的 HTTP 头。在 1.1 中,增加了内存缓存,现在,您可以使用本地内存或保存在 SQL Server 或 Redis 中的分布式缓存。

标准

标准很重要;这就是为什么我们有这么多。的最新版本.NET 标准是第 2 版.NET Core 2 实现了这一点。思考的好方法.NET 标准是一个类要实现的接口。接口将定义一个抽象的应用编程接口,但是这个应用编程接口的具体实现将留给从它继承的类。另一种思考方式是类似于 HTML5 标准,它被不同的网络浏览器所支持。

版本 2.NET 标准是通过查看.NET 框架和 Mono。这个标准是由.NET Core 2,这就是它比版本 1 包含更多 API 的原因。的 4.6.1 版.NET 框架也实现了.NET 标准 2,并且有工作支持最新版本的.NET 框架、UWP 和 Xamarin(包括 Xamarin。表单)。

还有新的 XAML 标准,旨在寻找 Xamarin 之间的共同点。表格和 UWP。希望未来包括Windows Presentation Foundation(WPF)。由于这是一本关于 web 应用的书,我们将不讨论 XAML 和本地用户界面。

如果您创建使用这些标准的库和包,那么它们将在支持它们的所有平台上工作。作为一个简单使用库的开发人员,您不需要担心这些标准。这只是意味着您更有可能在您正在使用的平台上使用您想要的包。

新的 C#特性

不仅仅是框架和库被开发了。底层语言也增加了一些不错的新特性。这里我们将重点讨论 C#语言,因为它是公共语言运行时 ( CLR )最流行的语言。其他选项包括 Visual Basic 和函数式编程语言 F#。

C#是一种很好的工作语言,尤其是与 JavaScript 这样的语言相比。尽管 JavaScript 很棒有很多原因(比如它的无处不在和可用框架的数量),但是语言的优雅和设计并不是其中之一。我们将在本书的后面介绍 JavaScript。

这些新特性中的许多只是语法糖,这意味着它们没有添加任何新功能。它们只是提供了一种更简洁、更易于阅读的方式来编写做同样事情的代码。

C# 6

虽然 C#的最新版本是 7,但是 C# 6 中有一些非常方便的特性经常没有被充分利用。此外,7 中的一些新增加的内容是对 6 中增加的功能的改进,没有任何上下文就没有多大意义。我们将在这里快速介绍 C# 6 的一些特性,以防您不知道它们有多有用。

字符串插值

字符串插值是熟悉的字符串格式方法的一个更优雅和更容易操作的版本。您现在可以直接将参数嵌入到字符串中,而不是单独提供要嵌入到字符串占位符中的参数。这样可读性更强,更不容易出错。

让我们用一个例子来证明这一点。考虑以下在字符串中嵌入异常的代码:

catch (Exception e)

{

    Console.WriteLine("Oh dear, oh dear! {0}", e);

}

这将在字符串中由零标记的位置嵌入第一个(仅在本例中)对象。这看起来可能很简单,但是如果您有很多对象,并且想在开始时添加另一个对象,它会很快变得复杂。然后,您必须正确地对所有占位符重新编号。

相反,您现在可以在字符串前面加上一个美元字符,并将对象直接嵌入其中。这在下面的代码中显示,其行为与前面的示例相同:

catch (Exception e)

{

    Console.WriteLine($"Oh dear, oh dear! {e}");

}

异常上的ToString()方法输出所有需要的信息,包括名称、消息、堆栈跟踪和任何内部异常。不需要手动解构;如果你错过了,你甚至会错过一些东西。

您也可以使用与以前相同的格式字符串。考虑以下以自定义方式格式化日期的代码:

Console.WriteLine($"Starting at: {DateTimeOffset.UtcNow:yyyy/MM/dd HH:mm:ss}");

当构建这个特性时,语法略有不同。因此,要警惕任何可能不正确的旧博文或文档。

空条件

空条件运算符是简化空检查的一种方式。现在可以对 null 进行内联检查,而不是使用 if 语句或三元运算符。这使得它更容易在更多的地方使用,并有望帮助您避免可怕的空引用异常。

您可以避免执行手动空值检查,如以下代码所示:

int? length = (null == bytes) ? null : (int?)bytes.Length;

现在可以通过添加问号将其简化为以下语句:

int? length = bytes?.Length;

异常过滤器

您可以使用when关键字更容易地过滤异常。您不再需要捕捉您感兴趣的每一种异常,然后在catch块中手动过滤它。这是 VB 和 F#中已经存在的一个特性,所以 C#终于赶上来了,这很好。

这种方法有一些小的好处。例如,如果您的过滤器不匹配,那么异常仍然会被同一try语句中的其他 catch 块捕获。你也不需要记得重新抛出异常来避免被吞噬。这有助于调试,因为 Visual Studio 将不再像您使用throw时那样崩溃。

例如,您可以检查异常中是否有消息,并以不同的方式处理它,如下所示:

catch (Exception e) when (e?.Message?.Length > 0)

在开发该功能时,使用了不同的关键字(if)。所以要小心网上的任何旧信息。

需要记住的一点是,依赖特定的异常消息是脆弱的。如果您的应用是本地化的,那么消息可能与您期望的语言不同。在异常过滤之外也是如此。

异步可用性

另一个小改进是可以在catchfinally块内使用await关键字。当这个非常有用的特性被添加到 C# 5 中时,这在最初是不允许的。关于这一点没有更多的话要说了。实现是复杂的,但是你不需要担心这个,除非你对内部感兴趣。从开发人员的角度来看,它只是工作,就像这个简单的例子:

catch (Exception e) when (e?.Message?.Length > 0)

{

    await Task.Delay(200);

}

这个特性在 C# 7 中得到了改进,请继续阅读。你会看到asyncawait在这本书里用了很多。异步编程是提高性能的好方法,而不仅仅是从 C#代码中提高性能。

表情体

表达式主体允许您使用 lambda arrow 运算符(=>)将表达式分配给方法或 getter 属性,您可能从流利的 LINQ 语法中熟悉这一点。您不再需要提供完整的语句或方法签名和正文。这个特性在 C# 7 中也得到了改进,所以请看下一节中的例子。

例如,getter 属性可以这样实现:

public static string Text => $"Today: {DateTime.Now:o}";

方法可以用类似的方式编写,例如下面的示例:

private byte[] GetBytes(string text) => Encoding.UTF8.GetBytes(text);

C# 7

C#语言的最新版本是 7,在可读性和易用性方面还有更多改进。我们将在这里介绍一些更有趣的变化。

文字

在代码中指定文字值时,还有一些小的附加功能和可读性增强。您可以指定二进制文字,这意味着您不必再使用不同的基来表示它们。您也可以将下划线放在文字中的任何位置,以便于阅读数字。下划线被忽略,但允许您将数字分成常规分组。这特别适合新的二进制文字,因为它可能非常冗长,列出了所有的 0 和 1。

以下面的例子为例,它使用新的0b前缀来指定一个二进制文字,该文字将被呈现为字符串中的一个整数:

Console.WriteLine($"Binary solo! {0b0000001_00000011_000000111_00001111}");

您也可以用其他基数来实现这一点,比如这个整数,它被格式化为使用千位分隔符:

Console.WriteLine($"Over {9_000:#,0}!"); // Prints "Over 9,000!"

元组

C# 7 的一大新特性是支持元组。元组是一组值,现在可以直接从方法调用中返回它们。您不再局限于返回单个值。以前,您可以用一些次优的方法来解决这个限制,包括创建一个自定义的复杂对象来返回,也许用一个普通的旧 C#对象 ( POCO )或数据传输对象 ( DTO ),这是一回事。您也可以使用refout关键字传入一个引用,虽然语法有所改进,但仍然不是很好。

C# 6 中有System.Tuple,但并不理想。这是一个框架特性,而不是语言特性,项目只是编号,没有命名。使用 C# 7 元组,您可以命名对象,它们是匿名类型的绝佳选择,尤其是在 LINQ 查询表达式 lambda 函数中。例如,如果您只想处理可用数据的一个子集,也许在用操作系统过滤数据库表时,比如实体框架,那么您可以为此使用元组。

下面的示例从方法返回一个元组。您可能需要添加System.ValueTuple NuGet 包来实现这一点:

private static (int one, string two, DateTime three) GetTuple()

{

    return (one: 1, two: "too", three: DateTime.UtcNow);

}

您还可以在字符串插值中使用元组,所有值都将被呈现,如下所示:

Console.WriteLine($"Tuple = {GetTuple()}");

输出变量

如果您想将参数传递给一个方法进行修改,那么您总是需要首先声明它们。这不再是必需的,您可以简单地在传入变量时声明它们。还可以使用下划线声明要丢弃的变量。如果您不想使用返回值,例如,在本机框架数据类型的一些 try parse 方法中,这尤其有用。

这里,我们解析一个日期而不首先声明dt变量:

DateTime.TryParse("2017-08-09", out var dt);

在这个例子中,我们测试一个整数,但是我们不关心它是什么:

var isInt = int.TryParse("w00t", out _);

参考

现在,您可以通过引用方法返回值并使用它们。这有点像在 C 语言中使用指针,但更安全。例如,您只能返回传递给方法的引用,而不能修改引用以指向内存中的不同位置。这是一个非常专业的特性,但是在某些特殊情况下,它可以显著提高性能。

考虑以下方法:

private static ref string GetFirstRef(ref string[] texts)

{

    if (texts?.Length > 0)

    {

        return ref texts[0];

    }

    throw new ArgumentOutOfRangeException();

}

您可以像这样调用这个方法,第二个控制台输出行将会以不同的方式出现(one而不是1):

var strings = new string[] { "1", "2" };

ref var first = ref GetFirstRef(ref strings);

Console.WriteLine($"{strings?[0]}"); // 1

first = "one";

Console.WriteLine($"{strings?[0]}"); // one

模式

另一个重要的补充是你现在可以在 C# 7 中使用is关键字匹配模式。这简化了空值和类型匹配的测试。它还可以让您轻松使用强制转换值。这是使用完全多态性的更简单的替代方法(在这种情况下,派生类可以被视为基类并重写方法)。但是,如果你控制了代码库,并且能够适当地使用多态性,那么你仍然应该这样做,并且遵循良好的面向对象编程 ( OOP )原则。

在以下示例中,模式匹配用于分析未知对象的类型和值:

private static int PatternMatch(object obj)

{

    if (obj is null)

    {

        return 0;

    }

    if (obj is int i)

    {

        return i++;

    }

    if (obj is DateTime d ||

       (obj is string str && DateTime.TryParse(str, out d)))

    {

        return d.DayOfYear;

    }

    return -1;

}

switch语句的情况下也可以使用模式匹配,可以打开非原语类型,比如自定义对象。

更多表情体

表达式主体是从 C# 6 中的产品扩展而来的,现在您可以在更多的地方使用它们,例如,作为对象构造函数和属性设置器。在这里,我们扩展了前面的示例,将我们之前刚刚读到的属性的值设置包括在内:

private static string text;

public static string Text

{

    get => text ?? $"Today: {DateTime.Now:r}";

    set => text = value;

}

更多异步改进

async方法可以返回的内容有一些小的改进,虽然很小,但在某些情况下可以提供很大的性能提升。您不再需要返回一个任务,如果这个值已经存在的话,这个任务将是有益的。这样可以减少使用async方法和创建任务对象的开销。

Java Script 语言

你不能写一本关于网络应用的书而不涉及 JavaScript。到处都是。

如果你写了一个对每个请求都进行全页面加载的 web 应用,而它不是一个简单的内容站点,那么它会感觉很慢。然而,用户期望响应速度。

如果你是一个后端开发人员,那么你可能会认为你不必担心这个。但是,如果您正在构建一个应用编程接口,那么您可能想让它易于使用 JavaScript,并且您需要确保您的 JSON 被正确且快速地序列化。

即使你正在浏览器中运行的 JavaScript(或 TypeScript)中构建一个单页应用 ( SPA ),服务器仍然可以发挥关键作用。您可以使用 SPA 服务在服务器上运行 Angular 或 React 并生成初始输出。这可以提高性能,因为浏览器有东西要立即呈现。例如,有一个名为 React.NET 的项目将 React 与 ASP.NET 整合在一起,它支持 ASP.NET Core。

如果你一直在努力跟上.NET 世界,那么 JavaScript 就在另一个层面上了。似乎每周都有新的东西,这可能会导致框架疲劳和选择悖论。可供选择的东西太多了,你不知道该选什么。

我们将在本书的后面介绍一些更现代的实践,并展示它们可以带来的改进性能。我们将关注服务人员,并展示如何使用他们将工作移动到浏览器的背景中,使其感觉对用户更有响应。

摘要

在这一介绍性章节中,您看到了对中发生的变化的简要但高级的总结.NET Core 2 和 ASP.NET Core 2,与以前的版本相比。你也知道.NET 标准 2 及其用途。

我们展示了 C# 6 和 C# 7 中可用的一些新特性的例子。这些对于让你用更少的资源写更多的东西,让你的代码更易读、更容易维护是非常有用的。

最后,我们谈到了 JavaScript,因为它无处不在,这毕竟是一本关于网络应用的书。此外,这是一本关于一般 web 应用性能改进的书,无论使用什么语言或框架,许多课程都是适用的。

在下一章中,您将看到为什么性能很重要,并了解新的.NET Core 堆栈适合在一起。我们还将看到可用的工具,并通过图表了解硬件性能。