前言

当将 Web 应用从 .NET Framework 升级到 .NET Standard 或 .NET 时,您可能需要处理 System.Web。在升级过程中,System.Web 的引用要么被移除,要么会导致编译时错误。

根据场景和迁移阶段,有几种选择可以解决此问题。

1. 用 IHttpContextAccessor 替换(仅限 .NET)

在迁移到 .NET 时,您会发现 HttpContext.Current 不再存在。您可以在构造函数中使用 IHttpContextAccessor 并通过 httpContextAccessor.HttpContext 访问它。

public class SomeService
{
    public void DoSomething()
    {
        var httpContext = HttpContext.Current;
        // 其余代码...
    }
}
❌坏例子 - 一个 .NET Framework 代码片段,演示在方法中使用 HttpContext.Current。
public class SomeService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public SomeService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void DoSomething()
    {
        var httpContext = _httpContextAccessor.HttpContext;
        // 其余代码...
    }
}
✅好例子 - 代码片段,显示在 .NET 服务类中用 IHttpContextAccessor 替换 HttpContext.Current。

2. 抽象功能

您还可以抽象出从 HttpContext 需要的内容,这在您希望在非 Web 环境(如控制台应用)中运行代码或希望保持对 .NET Framework 的依赖时非常有用。

如果您同时针对 .NET 和 .NET Framework,我们需要为 .NET Framework 添加回 System.Web 引用。我们通过更新 csproj 文件来实现这一点。

<!-- .NET Framework 对 HttpContext 的引用 -->
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
    <Reference Include="System.Web" />
</ItemGroup>

<!-- .NET 对 IHttpContextAccessor 的引用,如果项目是 WebApp,则已包含 -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
在 .NET Framework 4.7.2 项目中条件包含 "System.Web" 引用。

接下来,定义一个接口。在这个例子中,我们只公开当前经过身份验证的用户。我们称之为 IApplicationContext,因为它包含当前请求的上下文,无论是来自 HttpContext 还是其他地方,比如后台作业或控制台应用。

public interface IRequestContext
{
    string GetCurrentUsername();
}
IRequestContext 接口,用于检索当前用户的用户名。

下面,您可以看到多个 IRequestContext 的实现示例。您可能需要在相应平台的 Web 应用项目中进行实现。

#if NETFRAMEWORK

// 用于 .NET Framework
public sealed class LegacyHttpRequestContext : IRequestContext
{
    public string GetCurrentUsername()
        => HttpContext.Current?.User?.Identity?.Name;
}

#else // 或者 #if NET

// 用于 .NET Core 和 .NET
public sealed class HttpRequestContext : IRequestContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public HttpRequestContext(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public string GetCurrentUsername()
        => _httpContextAccessor.HttpContext.User.Identity?.Name;
}

#endif

// 用于后台作业、控制台应用、MAUI 等。
public sealed class BackgroundJobRequestContext : IRequestContext
{
    private readonly string _username;

    public BackgroundJobRequestContext(string username)
    {
        _username = username;
    }

    public string GetCurrentUsername() => _username;
}
针对不同环境(.NET Framework、.NET Core 和非 Web 上下文)的 `IRequestContext` 接口的多种实现。
注意:如果上述代码需要位于一个多目标项目中,可以使用 `#if NET472_OR_GREATER` 编译指示符来专门针对 .NET Framework 代码。

3. 抽象 HttpContext 访问器(仅限 .NET Framework)

对于严重依赖 HttpContext 且抽象 HttpContext 不切实际的项目,可以在 .NET Framework 中模仿 IHttpContextAccessor 的行为。虽然 IHttpContextAccessor 对于 .NET 应用程序可用,但对于 .NET Framework 应用程序并非如此。

namespace Microsoft.AspNetCore.Http
{
    // 确保仅在 .NET Framework 中使用,.NET 已有其自身的实现。
#if NET472_OR_GREATER
    // 与 .NET 的 IHttpContextAccessor 接口相同的接口。
    public interface IHttpContextAccessor
    {
        HttpContext HttpContext { get; }
    }

    // .NET Framework 的 IHttpContextAccessor 接口的简单实现
    public sealed class LegacyHttpContextAccessor : IHttpContextAccessor
    {
        public HttpContext HttpContext => HttpContext.Current;
    }
#endif
}
特定于 .NET Framework 的 IHttpContextAccessor 接口实现,仅在目标框架为 .NET 4.7.2 或更高版本时可用。

注意:确保您的 .csproj 配置正确。不要使用

<!-- .NET Framework 对 HttpContext 的引用 -->
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
    <Reference Include="System.Web" />
</ItemGroup>

<!-- .NET 对 IHttpContextAccessor 的引用,如果项目是 WebApp,则已包含 -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

4. ⚠️ 最后手段:System.Web 适配器

作为最后的手段,可以使用 System.Web 适配器。这些适配器允许您在不需要移植代码的情况下访问 HttpContext.Current。请注意,虽然这看起来像是一个简单的解决方案,但并非所有项目都能在不出现问题的情况下采用此方法。因此,只有在所有其他选项都不可行时才应使用此方法。

有关详细信息,请参阅 Microsoft Learn 上的官方 Microsoft 文档,了解 System.Web 适配器

注意:上述策略并非互斥,可以根据您的具体需求和约束条件结合使用。目标是使代码更具适应性,为迁移到 .NET 或 .NET Standard 做好准备。