dotnet-architecture / HealthChecks

Experimental Health Checks for building services, such as with ASP.NET Core
Other
453 stars 124 forks source link

Support of DI #23

Closed ycrumeyrolle closed 7 years ago

ycrumeyrolle commented 7 years ago

Check may support DI on ctor.

bradwilson commented 7 years ago

@ycrumeyrolle Please take a look at PR #50, as this makes significant changes in support of DI. I hope based on our previous discussions you will probably be happier with the post-DI design than the pre-DI design. :) Thanks!

ycrumeyrolle commented 7 years ago

@bradwilson It is much better, but... I am wondering how to implement an HealthCheck with an existing registered transient service, requiring parameters to check.

services.AddTransient<IWonderfulService, WonderfulService>();

where IWonderfulService looks like

public interface IWonderfulService
{
  void DoSomething(string someValue);
}

I would like to avoid to wrap existing services into a HealthCheckAdapter or something similar.

Adding a HealthCheckContext parameter to the IHealthCheck.CheckHealthAsync() would allow to pass values. Maybe something similar to the AuthZ service ?

bradwilson commented 7 years ago

I'm not sure what you're after here. You should just be able to ask for the IWonderService constructor argument to get it for DI. What is the purpose of the context?

ycrumeyrolle commented 7 years ago

What I would like to do :

healthCheckBuilder.AddMyService(testingValue: "special value for test purpose")

I have this kind of interface (basically, a method with value parameter)

// This interface is an existing interface and should not be modified. 
public interface IMyService 
{
  void DoSomething(string someValue);
}

I see two ways to implement a IHealthCheck for this service. With a DI-friendly HealthCheck :

public class MyServiceHealthCheckByDI : IHealthCheck
{
  private readonly IMyService _myService;
  public MyServiceHealthCheck(IMyService service)
  {
    _myService = service;
  }

  public async ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken))
  {
    try
    {
      // How can set this value with the HealthCheckBuilder? 
      var testingValue = "special value for test purpose";
      _myService.DoSomething(testingValue);
      return HealthCheckResult.Healthy("OK");
    }
    catch(Exception e)
    {
      return HealthCheckResult.Unhealthy(e.ToString());
    }
  }
}

I do not know how to pass the required value to the HealthCheck.

With a non DI-Friendly HealthCheck :

public class MyServiceHealthCheckWithoutDI : IHealthCheck
{
  private readonly IMyService _myService;
  private readonly string _testingValue;
  public MyServiceHealthCheck(string testingValue)
  {
    _testingValue = testingValue;
    // Not DI Friendly
    _myService = new MyServiceImplementation(new MyServiceImplementationDependency(new MyServiceImplementationDependencyDependency(...)));
  }

  public async ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken))
  {
    try
    {
      _myService.DoSomething(_testingValue);
      return HealthCheckResult.Healthy("OK");
    }
    catch(Exception e)
    {
      return HealthCheckResult.Unhealthy(e.ToString());
    }
  }
}

I can pass the value parameter with the HealthCheck constructor, but it is not DI-compliant anymore...

bradwilson commented 7 years ago

Registration of services is outside the scope of health checks.

That said, there are lambda versions of DI registration that do what you want:

public static IServiceCollection RegisterMyService(this IServiceCollection serviceCollection, string testingValue)
    => serviceCollection.AddSingleton<IMyService>(serviceProvider => new MyServiceImplementation(testingValue));