在其他服务中访问MVC DI服务

时间:2017-10-06 08:28:09

标签: c# asp.net-core .net-core asp.net-core-webapi asp.net-core-2.0

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

我已成功完成此工作,中间件正在记录服务启动时间并记录响应时间,我们的运行状况监视器能够通过调用我们的StatusController来解析这些值,public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddApplicationPart(Assembly.Load(new AssemblyName("HealthApiLibrary"))); //Bring in the Controller for HealthAPI; services.AddSingleton<HealthApiService>(); } 有许多操作返回IActionResult JSON回复。

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

services.AddSingleton<HealthApiService>();

然而,在重构阶段,我想通过执行以下操作来清理它:

1)将services.AddHealthApi();重构为services.AddHealthApi();(我们尚未对此做任何工作,但在回答此问题时仍可能相关)

2)作为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; } } 电话的一部分加载到我的StatusController中。

我尝试了以下内容:

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

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

hlines

2 个答案:

答案 0 :(得分:2)

<强> 1。依赖注入

您收到异常,因为服务集合中未注册IMvcBuilder。将此类型添加到集合中是没有意义的,因为它仅在启动期间使用。

<强> 2。扩展方法

您可以创建一个扩展方法来实现您想要的方法。

public static class AddHealthApiExtensions
{
    public static void AddHealthApi(this IServiceCollection services)
    {
        services.AddSingleton<HealthApiService>();
    }
}

第3。 Assembly.Load

查看@Tseng的评论。

答案 1 :(得分:2)

根据我收集的内容,您尝试允许最终用户向HealthApiService提供自己的依赖项。这通常使用扩展方法和一个或多个构建器模式来完成。这不是DI问题,而是应用程序组合问题。

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

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

    }
}

扩展方法

扩展方法对默认依赖项有一个重载,对任何自定义依赖项都有一个重载。

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; });
    }
}

生成器

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

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);
    }
}

用法

    // 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()));
    }

加成

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

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);
}

然后可以像

一样使用
services.AddHealthApi(healthApi => 
    healthApi.WithFoo(foo => foo.WithFooey(new MyFooey)));

更多

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

参考

DI Friendly Library by Mark Seemann