65.9K
CodeProject 正在变化。 阅读更多。
Home

一次 Windows 10 更新如何可能破坏了你的 .NET 应用

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2018年8月2日

CPOL
viewsIcon

11076

在这里,我将介绍微软最近的 Windows 更新所带来的影响,以及如何为因此次发布而导致的崩溃实施变通方案,以及如何使用 Sentry 监控错误并在不费吹灰之力的情况下迭代你的应用。

Windows 10 更新:好消息与坏消息

几个月前,微软发布了 Windows 10 的 2018 年 4 月更新 (1803)。此更新中包含 .NET Framework 的升级,使其版本达到 4.7.2。

在这里,我将介绍微软最近的 Windows 更新所带来的影响,以及如何为因此次发布而导致的崩溃实施变通方案,以及如何使用 Sentry 监控错误并在不费吹灰之力的情况下迭代你的应用。

Windows 10 的发布是 .NET Framework 4 的就地安装,这意味着你在近 10 年内构建的任何 .NET 应用程序都将运行在其上。也就是说,如果你的应用程序编译目标是以下 .NET Framework 版本:4.0、4.5、4.5.1、4.5.2、4.6、4.6.1、4.6.2、4.7、4.7.1、4.7.2。

惊喜!你昨天甚至十年前创建的应用程序,可能会因为操作系统更新而突然开始崩溃。

例如,这段代码片段可以针对 .NET Framework 4.0(2010 年 4 月发布)

class Program
{
    static void Main()
    {
            new System.Data.SqlClient.SqlConnection("Data Source=;")
            {
                ConnectionString = null
            };
    }
}

好消息是,尽管目标是古老的 .NET Framework 版本,这段代码在接下来的八年中在后续的 .NET 更新上都能成功运行。

坏消息是,这段在 .NET 4.7.2 上执行的代码会抛出以下异常

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.   
   at System.Data.SqlClient.SqlConnection.CacheConnectionStringProperties()

罪魁祸首

那么,究竟是什么导致了这个问题?

在调查过程中,我注意到如果使用连接字符串实例化 SqlConnection,然后再次调用 `ConnectionString` setter,就会抛出异常。简单地两次调用 setter 也会抛出异常。

使用反编译工具,我们可以观察到是哪个方法抛出了异常。在这里,我们看到构造函数和 setter 在 ConnectionString 中调用了什么

private void CacheConnectionStringProperties()
  {
    SqlConnectionString connectionOptions = this.ConnectionOptions as SqlConnectionString;
    if (connectionOptions != null)
      this._connectRetryCount = connectionOptions.ConnectRetryCount;
    if (this._connectRetryCount != 1 || !ADP.IsAzureSqlServerEndpoint(connectionOptions.DataSource))
      return;
    this._connectRetryCount = 2;
  }

将其与参考源代码进行比较,该源代码尚未包含 4.7.2

 private void CacheConnectionStringProperties() {
      SqlConnectionString connString = ConnectionOptions as SqlConnectionString;
      if (connString != null) {
          _connectRetryCount = connString.ConnectRetryCount;
      }
  }

显然,对 ADP.IsAzureSqlServerEndpoint 的调用是新引入的。在访问 ConnectionRetryCount 之前有一个空检查,但在 DataSource 之前没有保护,这会导致抛出异常。幸运的是,只有当我们尝试重新分配 ConnectionString 值时才会发生错误。

此问题已报告给微软,他们已发布更新表示正在修复。

解决方法

别担心!你可以通过使用 SqlConnection 的新实例而不是尝试重用现有实例来轻松解决此问题。只需确保连接字符串不要设置多次。

using (var first = new SqlConnection("Data Source=first;")) { }

using (var second = new SqlConnection("Data Source=second;")) { }

使用 .NET Core 避免自动框架更新

希望这个(非常真实)的例子能说明,即使是多年来一直运行无 bug 的稳定代码,在运行于新版本的 .NET Framework(包括作为自动操作系统更新一部分引入的版本)时,仍然可能会出错。糟糕 🙀

一种防止自动框架更新的方法是使用 .NET Core

微软在不到两年前发布了 .NET Core。与完整的 .NET Framework 相比,.NET Core 是一项非常年轻的技术。但 .NET Core 的优势之一是安装是并行的。换句话说,新安装不会影响旧安装,你可以指定代码要运行的确切版本。毫不奇怪,版本选择可以让你避免此类问题。

查看 .NET Core 并通过微软的一些综合教程了解更多信息。

在用户甚至不知道有问题之前修复 .NET 错误

无论你使用 .NET Core、.NET Framework 还是 Mono,意想不到的 bug 都会破坏任何人的好心情。

为了确保你是第一个发现 bug 并了解上下文以便快速解决问题的人,你需要一个专为 .NET 开发者构建并无缝融入你现有工作流程的错误跟踪工具。

Sentry 是一个开源的工作流程生产力平台,它聚合来自任何 .NET 或 C# 应用程序的错误和崩溃,并提供完整的应用程序逻辑、深入的上下文以及跨整个堆栈的实时可见性。

Sentry 的核心价值包括公开每个错误的完整堆栈跟踪,为每个异常提供详细上下文,显示作为导致错误的线索的事件和前置步骤,并将错误与特定的提交和作者关联起来。Sentry 最近还添加了有关引发事件设备的运行时和操作系统版本的清晰信息,以及为每个问题指定负责人,这样就不会有人收到过多邮件。

让我们更新之前的示例以使用 Sentry 捕获错误

using System;
using System.Data.SqlClient;
using SharpRaven;
using SharpRaven.Data;

class Program
{
    static void Main()
    {
        var client = new RavenClient("https://your-dsn@sentry.io/project-id");
        try
        {
            var connection = new SqlConnection("Data Source=;");
            connection.ConnectionString = null;
        }
        catch (Exception ex)
        {
            client.Capture(new SentryEvent(ex));
        }
    }
}

运行这段代码(你可以从GitHub 下载)将导致异常记录到 Sentry。

Sentry 中捕获的异常如下所示

当然,此页面上有大量信息和上下文,但最重要信息一目了然。注意顶部的图标:运行时和操作系统。

首先,我们看到 .NET Framework 图标,后面跟着版本 4.7.2,考虑到此错误是随该版本引入的,这很有道理。我们还看到操作系统:Windows,版本 10.0.17134。正如微软所说,此数字表示:“Windows 10 build 17134(也称为 4 月更新或版本 1803)。”

为了显示这些信息,Sentry 依赖其 .NET SDK 提供数据,包括 .NET Framework 发布版本号。对于此 Windows 更新,Sentry 知道发布 ID 461808 意味着 .NET Framework 4.7.2。

除了报告 .NET Framework,事件详情还包括 .NET Core 和 Mono,它们可以从 Windows、macOS 和 Linux 报告

在 Mono 下于 Linux 上运行的 .NET Framework 应用程序会将操作系统报告为 Unix。在上面的示例中,我使用的是适用于 Linux 的 Windows 子系统 (WSL) 上的 Ubuntu,因此内部版本号与 Windows 相同

Linux Tampere 4.4.0-17134-Microsoft [#48-Microsoft](https://paper.dropbox.com/?q=%2348-Microsoft) 2018 年 4 月 27 日星期五 太平洋标准时间 18:06:00 x86_64 x86_64 x86_64 GNU/Linux

另一个有趣的案例是针对 .NET Core 并运行在 macOS 上的应用程序。事件详情将简单地报告为 Darwin,这可能是 macOS 或 iOS。如果有足够的需求,我们可以通过查找系统中可以告诉我们正在运行哪个发行版的特定文件来进一步改进这一点。

Sentry 随时待命

一如既往,你可以为你的 .NET 应用试用 Sentry。平均而言,它将应用程序错误的解决时间从五小时缩短到五分钟。通过在现有工作流程中解决生产问题,每年为你的 .NET 应用程序修复看不见的 bug,节省数周时间。Sentry 是100% 开源的,两分钟即可设置完成,深受 50 万开发人员的喜爱。

就是这样。祝你错误监控愉快!

一次 Windows 10 更新如何可能破坏了你的 .NET 应用 - CodeProject - 代码之家
© . All rights reserved.