【问题标题】:How is Pure DI implemented with Razor Pages如何使用 Razor Pages 实现 Pure DI
【发布时间】:2022-02-16 17:32:00
【问题描述】:

这个问题与my previous question about Razor Components 类似,但是这个问题是关于Razor Pages,它需要不同的拦截点。

我正在使用Dependency Injection Principles, Practices, and Patterns (DIPP&P) 一书中解释的纯 DI 方法制作 ASP.NET Core 应用程序。我的应用程序的一部分有一个 Web API 控制器。要使用我的控制器实现Pure DI,我可以轻松地按照 DIPP&P 的第 7.3.1 节“创建自定义控制器激活器”来创建控制器激活器类 similar to the example found in DIPP&P。这是通过实现IControllerActivator 并在create 方法中组成我的组合根来完成的。

我的应用程序还将包含 Razor Pages。我想继续使用 Pure DI 方法,但我找不到任何有关如何执行此操作的示例。我的假设是我需要创建一个RazorPageActivator 类,该类实现IRazorPageActivator 并将我的组合根添加到Activate 方法中。但是,在查看了在 ASP.NET Core GitHub 中找到的 RazorPageActivator 类之后,它看起来非常复杂,我担心如果我通过创建自己的实现 IRazorPageActivator 的类来拦截它(或覆盖它?)事情会中断,我会一团糟。

我的问题是,如果可能的话,如何使用 Razor Pages 实现 Pure DI?

【问题讨论】:

  • 它是重复的,没有重大变化或澄清。 stackoverflow.com/questions/71074871/…
  • 嗨 - 如果您阅读 cmets 以回答另一个问题,您会明白我为什么再次发布此内容。对我上一个版本的响应是针对 Razor 组件,而不是 Razor 页面。我们决定保留该回复,因为如果有帮助并再次重新发布我的原始问题。
  • 不,这不是重复的。我更新了问题以明确表明不是。这个问题是关于 Razor Pages,而另一个问题是关于 Razor Components,这是一种不同的技术。我投票支持重新开放。

标签: c# asp.net asp.net-core dependency-injection razor-pages


【解决方案1】:

使用 Razor Pages,IPageModelActivatorProvider 充当您的 Composition Root 的 Composer。这是一个基于默认 Visual Studio (2019) Razor Pages 项目模板的示例。

让我们从自定义 IPageModelActivatorProvider 开始,它充当您的 Composer,它是您的 Composition Root 的一部分:

public class CommercePageModelActivatorProvider
    : IPageModelActivatorProvider, IDisposable
{
    // Singletons
    private readonly ILoggerFactory loggerFactory;

    public CommercePageModelActivatorProvider(ILoggerFactory loggerFactory) =>
        this.loggerFactory = loggerFactory;

    public Func<PageContext, object> CreateActivator(
        CompiledPageActionDescriptor desc) =>
        c => this.CreatePageModelType(c, desc.ModelTypeInfo.AsType());

    public Action<PageContext, object> CreateReleaser(
        CompiledPageActionDescriptor desc) =>
        (c, pm) => (pm as IDisposable)?.Dispose();

    private object CreatePageModelType(PageContext c, Type pageModelType)
    {
        // Create Scoped components
        var context = new CommerceContext().TrackDisposable(c);

        // Create Transient components
        switch (pageModelType.Name)
        {
            case nameof(IndexModel):
                return new IndexModel(this.Logger<IndexModel>(), context);
            case nameof(PrivacyModel):
                return new PrivacyModel(this.Logger<PrivacyModel>());
            default: throw new NotImplementedException(pageModelType.FullName);
        }
    }

    public void Dispose() { /* Release Singletons here, if needed */ }

    private ILogger<T> Logger<T>() => this.loggerFactory.CreateLogger<T>();
}

请注意此实现的一些事项:

  • 这个类的结构与book's code samples中给出的非常相似。
  • 它实现了IDisposable 以允许处理它自己创建的单例。在这种情况下,它的构造函数中没有创建单例,因此不需要处理任何内容。 ILoggerFactory 是“外部所有”;它由框架创建,并将由框架处置(如果需要)。
  • 该类使用自定义TrackDisposable 扩展方法(稍后显示),该方法允许跟踪范围和瞬态依赖关系。 TrackDisposable 方法会将这些实例添加到请求中,并允许框架在请求结束时释放它们。
  • 这就是CreateReleaser 方法只处理页面本身的原因。当您跟踪它们以进行处置时,框架会完成所有其他已创建组件的处置。您还可以选择跟踪页面本身;在这种情况下,您可以将 CreateReleaser 委托留空。
  • 有一个方便的Logger&lt;T&gt;() 方法可以简化ILogger&lt;T&gt; 实现的创建。这些来自框架,由ILoggerFactory 创建。

这是TrackDisposable扩展方法:

public static class DisposableExtensions
{
    public static T TrackDisposable<T>(this T instance, PageContext c)
        where T : IDisposable
    {
        c.HttpContext.Response.RegisterForDispose(instance);
        return instance;
    }
}

最后缺少的基础设施是将您的CommercePageModelActivatorProvider 注册到框架的 DI 容器中。这是在 Startup 类中完成的:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();

        // Register your custom component activator here
        services.AddSingleton<
            IPageModelActivatorProvider, CommercePageModelActivatorProvider>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
    }
}

【讨论】:

  • 再次感谢您的详尽回答。我能够轻松地效仿你的榜样。您提到 CreateReleaser 方法通常可以返回一个空委托。看起来DefaultPageModelActivatorProvider.CreateReleaser() 方法委托给Dispose 方法。为什么不这样做?
  • 是的,你是对的。这样做会很好,因为这样可以使您不必跟踪页面本身。我会更新我的答案以反映这一点。
猜你喜欢
  • 1970-01-01
  • 2020-02-02
  • 2021-09-25
  • 2023-03-24
  • 2015-07-29
  • 1970-01-01
  • 1970-01-01
  • 2020-07-24
  • 1970-01-01
相关资源
最近更新 更多