在其他服务中访问 MVC DI 服务 [英] Accessing MVC DI Service inside other services

查看:19
本文介绍了在其他服务中访问 MVC DI 服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个 HealthAPI 类库,它为我们的 HealthMonitor 服务提供统计数据列表.

我已经成功地完成了这项工作,中间件正在记录服务启动时间和响应时间,我们的健康监视器能够通过调用我们的 StatusController 来解析这些值,它有许多返回 IActionResult JSON 响应的操作.

我们打算在我们所有的服务中重用它,因此选择将 API 控制器与 DI 服务和中间件一起保留在类库中,以使控制器可访问我最初做了以下操作.

public void ConfigureServices(IServiceCollection services){services.AddMvc().AddApplicationPart(Assembly.Load(new AssemblyName("HealthApiLibrary")));//为HealthAPI引入Controller;services.AddSingleton();}

但是在重构阶段,我想通过执行以下操作来稍微清理一下:

1) 将 services.AddSingleton<HealthApiService>(); 重构为 services.AddHealthApi();(我们还没有为此做任何工作,但仍然回答这个问题时可能是相关的)

2) 作为 services.AddHealthApi(); 调用的一部分加载到我的 StatusController 中.

我尝试了以下方法:

公共类 HealthApiService{public HealthApiService(IMvcBuilder mvcBuilder){mvcBuilder.AddApplicationPart(Assembly.Load(new AssemblyName("HealthApiLibrary")));//为HealthAPI引入ControllerResponseTimeRecords = new Dictionary();ServiceBootTime = DateTime.Now;}公共日期时间服务启动时间 { 获取;放;}公共字典ResponseTimeRecords { 获取;放;}公共字符串 ApplicationId { 获取;放;}}

然而,这只会产生以下错误:

InvalidOperationException:尝试激活HealthApiLibrary.Services.HealthApiService"时无法解析Microsoft.Extensions.DependencyInjection.IMvcBuilder"类型的服务.

解决方案

据我所知,您正试图允许最终用户向您的 HealthApiService 提供他们自己的依赖项.这通常是使用扩展方法和一个或多个构建器模式来完成的.不是DI问题,而是应用组合问题.

假设 HealthApiService 有 2 个依赖项,IFooIBar,并且您希望用户能够为每个依赖项提供他们自己的实现:

公共类 HealthApiService : IHealthApiService{public HealthApiService(IFoo foo, IBar bar){}}

扩展方法

扩展方法有一个用于默认依赖项的重载和一个用于任何自定义依赖项的重载.

公共静态类 ServiceCollectionExtensions{public static void AddHealthApi(this IServiceCollection services, Func表达式){如果(服务 == 空)throw new ArgumentNullException(nameof(services));如果(表达式 == 空)throw new ArgumentNullException(nameof(expression));var starter = new HealthApiServiceBuilder();var builder = expression(starter);services.AddSingleton(builder.Build());}public static void AddHealthApi(这个 IServiceCollection 服务){AddHealthApi(services, builder => { return builder; });}}

生成器

构建器有助于一次构建一个 HealthApiService 依赖项.它收集依赖项,然后在过程结束时 Build() 方法创建实例.

公共类 HealthApiServiceBuilder{私有只读 IFoo foo;私人只读 IBar 栏;public HealthApiServiceBuilder()//这些是可以覆盖的默认依赖项//单独由构建器: 这个(新的 DefaultFoo(), 新的 DefaultBar()){ }内部 HealthApiServiceBuilder(IFoo foo, IBar bar){如果 (foo == null)throw new ArgumentNullException(nameof(foo));如果(条==空)throw new ArgumentNullException(nameof(bar));this.foo = foo;this.bar = 酒吧;}public HealthApiServiceBuilder WithFoo(IFoo foo){返回新的 HealthApiServiceBuilder(foo, this.bar);}public HealthApiServiceBuilder WithBar(IBar bar){返回新的 HealthApiServiceBuilder(this.foo, bar);}public HealthApiService Build(){返回新的 HealthApiService(this.foo, this.bar);}}

用法

//这个方法被运行时调用.使用此方法向容器添加服务.public void ConfigureServices(IServiceCollection 服务){服务.AddMvc();//默认依赖services.AddHealthApi();//自定义依赖//services.AddHealthApi(healthApi =>//healthApi.WithFoo(new MyFoo()).WithBar(new MyBar()));}

奖金

如果您的默认 IFooIBar 实现有依赖项,您可以为每个实现创建一个构建器类.例如,如果 IFoo 有一个依赖项 IFooey,你可以为默认的 IFoo 实现创建一个构建器,然后重载 HealthApiServiceBuilder.WithFoo 带有表达式的方法:

public HealthApiServiceBuilder WithFoo(IFoo foo){返回新的 HealthApiServiceBuilder(foo, this.bar);}public HealthApiServiceBuilder WithFoo(Func 表达式){var starter = new FooBuilder();var builder = expression(starter);return new HealthApiServiceBuilder(builder.Build(), this.bar);}

然后可以像这样使用

services.AddHealthApi(healthApi =>healthApi.WithFoo(foo => foo.WithFooey(new MyFooey)));

更多

您需要在应用程序启动时注册但不希望最终用户与之交互的任何其他服务(例如,控制器)都可以在扩展方法内完成.

参考

马克西曼的 DI 友好图书馆

I am constructing a HealthAPI Class Library Which provides a list of statistics to our HealthMonitor Service.

I have successfully got this working, The Middleware is recording Service boot time and recording response times, our health monitor is able to parse these values via a call to a our StatusController which has a number of actions returning IActionResult JSON responses.

We intend to reuse this over all of our services so have opted to keep the API controller within the Class Library along with the DI Service and middleware, to make the Controller accessable I originally did the following.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddApplicationPart(Assembly.Load(new AssemblyName("HealthApiLibrary"))); //Bring in the Controller for HealthAPI;
    services.AddSingleton<HealthApiService>();
}

However at the refactoring stage I want to clean this up a little by doing the following:

1) Refactor services.AddSingleton<HealthApiService>(); into services.AddHealthApi(); (Which we have not done any work towards just yet, but still may be relevent when answering this question)

2) Load in my StatusController as part of the services.AddHealthApi(); call.

I have tried the following:

public class HealthApiService
{
    public HealthApiService(IMvcBuilder mvcBuilder)
    {
        mvcBuilder.AddApplicationPart(Assembly.Load(new AssemblyName("HealthApiLibrary"))); //Bring in the Controller for HealthAPI

        ResponseTimeRecords = new Dictionary<DateTime, int>();
        ServiceBootTime = DateTime.Now;
    }

    public DateTime ServiceBootTime { get; set; }
    public Dictionary<DateTime,int> ResponseTimeRecords { get; set; }
    public string ApplicationId { get; set; }
}

however this just generates the following error:

InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.DependencyInjection.IMvcBuilder' while attempting to activate 'HealthApiLibrary.Services.HealthApiService'.

解决方案

From what I gather, you are trying to allow the end user to supply their own dependencies to your HealthApiService. This is typically done using an extension method and one or more builder patterns. It is not a DI problem, but an application composition problem.

Assuming HealthApiService has 2 dependencies, IFoo and IBar, and you want users to be able to supply their own implementation for each:

public class HealthApiService : IHealthApiService
{
    public HealthApiService(IFoo foo, IBar bar)
    {

    }
}

Extension Method

The extension method has one overload for the default dependencies and one for any custom dependencies.

public static class ServiceCollectionExtensions
{
    public static void AddHealthApi(this IServiceCollection services, Func<HealthApiServiceBuilder, HealthApiServiceBuilder> expression)
    {
        if (services == null)
            throw new ArgumentNullException(nameof(services));
        if (expression == null)
            throw new ArgumentNullException(nameof(expression));

        var starter = new HealthApiServiceBuilder();
        var builder = expression(starter);
        services.AddSingleton<IHealthApiService>(builder.Build());
    }

    public static void AddHealthApi(this IServiceCollection services)
    {
        AddHealthApi(services, builder => { return builder; });
    }
}

Builder

The builder is what helps construct the HealthApiService one dependency at a time. It collects the dependencies and then at the end of the process the Build() method creates the instance.

public class HealthApiServiceBuilder
{
    private readonly IFoo foo;
    private readonly IBar bar;

    public HealthApiServiceBuilder()
        // These are the default dependencies that can be overridden 
        // individually by the builder
        : this(new DefaultFoo(), new DefaultBar()) 
    { }

    internal HealthApiServiceBuilder(IFoo foo, IBar bar)
    {
        if (foo == null)
            throw new ArgumentNullException(nameof(foo));
        if (bar == null)
            throw new ArgumentNullException(nameof(bar));
        this.foo = foo;
        this.bar = bar;
    }

    public HealthApiServiceBuilder WithFoo(IFoo foo)
    {
        return new HealthApiServiceBuilder(foo, this.bar);
    }

    public HealthApiServiceBuilder WithBar(IBar bar)
    {
        return new HealthApiServiceBuilder(this.foo, bar);
    }

    public HealthApiService Build()
    {
        return new HealthApiService(this.foo, this.bar);
    }
}

Usage

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        // Default dependencies
        services.AddHealthApi();

        // Custom dependencies
        //services.AddHealthApi(healthApi => 
        //    healthApi.WithFoo(new MyFoo()).WithBar(new MyBar()));
    }

Bonus

If your default IFoo or IBar implementations have dependencies, you can make a builder class for each one. For example, if IFoo has a dependency IFooey you can create a builder for the default IFoo implementation, then overload the HealthApiServiceBuilder.WithFoo method with an expression:

public HealthApiServiceBuilder WithFoo(IFoo foo)
{
    return new HealthApiServiceBuilder(foo, this.bar);
}

public HealthApiServiceBuilder WithFoo(Func<FooBuilder, FooBuilder> expression)
{
    var starter = new FooBuilder();
    var builder = expression(starter);
    return new HealthApiServiceBuilder(builder.Build(), this.bar);
}

This can then be used like

services.AddHealthApi(healthApi => 
    healthApi.WithFoo(foo => foo.WithFooey(new MyFooey)));

More

Any other services (for example, controllers) that you need to register at application startup that you don't want the end user to interact with can be done inside of the extension method.

Reference

DI Friendly Library by Mark Seemann

这篇关于在其他服务中访问 MVC DI 服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆