dotnet/systemweb-adapters 存储库中适配器的主要用例是帮助那些在想要迁移到 ASP.NET Core 时依赖类库中 System.Web 类型的开发人员。
适配器的一个重要功能是它们 允许同时从 ASP.NET Framework 和 ASP.NET Core 项目中使用该库。 将多个 ASP.NET Framework 应用更新到 ASP.NET Core 通常涉及中间状态,其中并非所有应用都已完全更新。 通过使用 System.Web 适配器,可以从 ASP.NET Core 调用方和尚未升级的 ASP.NET Framework 调用方使用库。
让我们看看使用适配器从 .NET Framework 迁移到 ASP.NET Core 的示例。
包
-
Microsoft.AspNetCore.SystemWebAdapters:此包用于支持库,提供可能依赖的 System.Web API,例如HttpContext以及其他相关 API。 此包面向 .NET Standard 2.0、.NET Framework 4.5+和 .NET 5+。 Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices:此包仅面向 .NET Framework,旨在为可能需要提供增量迁移的 ASP.NET Framework 应用程序提供服务。 这通常不应从库引用,而是从应用程序本身引用。Microsoft.AspNetCore.SystemWebAdapters.CoreServices:此包仅面向 .NET 6+,旨在为 ASP.NET Core 应用程序提供服务,以便配置System.WebAPI 的行为,并选择性地参与任何增量迁移的行为。 这通常不应从库引用,而是从应用程序本身引用。Microsoft.AspNetCore.SystemWebAdapters.Abstractions:此包是一个支持包,它为 ASP.NET Core 和 ASP.NET Framework 应用程序(例如会话状态序列化)使用的服务提供抽象。
转换为 System.Web.HttpContext
若要在 HttpContext 的两种表示形式之间进行转换,可以执行以下作:
- 隐式转换
HttpContext.AsSystemWeb()
- 隐式转换
HttpContext.AsAspNetCore()
这两种方法都将在请求期间使用缓存 HttpContext 表示形式。 这允许根据需要对HttpContext进行有针对性地重写。
示例:
ASP.NET 框架
请考虑一个控制器执行以下动作:
public class SomeController : Controller
{
public ActionResult Index()
{
SomeOtherClass.SomeMethod(HttpContext.Current);
}
}
然后,它在一个单独的程序集中具有逻辑,四处传递该 HttpContext,直到最后,某个内部方法对它执行一些逻辑,例如:
public class Class2
{
public bool PerformSomeCheck(HttpContext context)
{
return context.Request.Headers["SomeHeader"] == "ExpectedValue";
}
}
ASP.NET Core
若要在 ASP.NET Core 中运行上述逻辑,开发人员需要添加 Microsoft.AspNetCore.SystemWebAdapters 包,使项目能够在这两个平台上工作。
需要更新库才能了解适配器,但只需添加包和重新编译即可。 如果这些是系统唯一拥有 System.Web.dll的依赖项,则库将能够以 .NET Standard 2.0 为目标,以便在迁移时简化生成过程。
ASP.NET Core 中的控制器现在如下所示:
public class SomeController : Controller
{
[Route("/")]
public IActionResult Index()
{
SomeOtherClass.SomeMethod(HttpContext);
}
}
请注意,由于有一个 HttpContext 属性,因此可以传递该属性,但一般情况下,其表现不会有太大差异。 使用隐式转换,可以将 HttpContext 转换为适配器,然后以相同的方式通过使用代码的级别传递适配器。
单元测试
使用 System.Web 适配器的单元测试代码时,请记住一些特殊注意事项。
在大多数情况下,无需设置用于运行测试的其他组件。 但是,如果测试的组件使用 HttpRuntime,则可能需要启动 SystemWebAdapters 服务,如以下示例所示:
namespace TestProject1;
/// <summary>
/// This demonstrates an xUnit feature that ensures all tests
/// in classes marked with this collection are run sequentially.
/// </summary>
[CollectionDefinition(nameof(SystemWebAdaptersHostedTests),
DisableParallelization = true)]
public class SystemWebAdaptersHostedTests
{
}
[Collection(nameof(SystemWebAdaptersHostedTests))]
public class RuntimeTests
{
/// <summary>
/// This method starts up a host in the background that
/// makes it possible to initialize <see cref="HttpRuntime"/>
/// and <see cref="HostingEnvironment"/> with values needed
/// for testing with the <paramref name="configure"/> option.
/// </summary>
/// <param name="configure">
/// Configuration for the hosting and runtime options.
/// </param>
public static async Task<IDisposable> EnableRuntimeAsync(
Action<SystemWebAdaptersOptions>? configure = null,
CancellationToken token = default)
=> await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.ConfigureServices(services =>
{
services.AddSystemWebAdapters();
if (configure is not null)
{
services.AddOptions
<SystemWebAdaptersOptions>()
.Configure(configure);
}
})
.Configure(app =>
{
// No need to configure pipeline for tests
});
})
.StartAsync(token);
[Fact]
public async Task RuntimeEnabled()
{
using (await EnableRuntimeAsync(options =>
options.AppDomainAppPath = "path"))
{
Assert.True(HostingEnvironment.IsHosted);
Assert.Equal("path", HttpRuntime.AppDomainAppPath);
}
Assert.False(HostingEnvironment.IsHosted);
}
}
测试必须按顺序执行,而不是并行执行。 前面的示例演示如何通过将 XUnit 的选项DisableParallelization设置为 true. 此设置禁用特定测试集合的并行执行,确保该集合中的测试一个接一个运行,而无需并发。