Search code examples
asp.net-coredependency-injectionpreconditions

Check preconditions in ASP.NET Core service early


In my ASP.NET Core app (currently .NET 7), I have some services with system-specific preconditions. For example, one service might require specific fonts to be installed, another one might require an external tool like Ghostscript to be installed.

I am looking for the best way to check these preconditions early, not only when the service is called the first time. Here are the options I found so far, but they do all not meet my requirements:

  1. I could add my preconditions check in the Startup class, as this answer suggests. However, this feels wrong to me, since the precondition should be an internal information of the service implementation, and should not be placed outside the service class.

  2. I could write a static CheckPreconditions() method in my service and call that method from the Startup. But this introduces exactly the same problem as with option 1.

  3. I could add the precondition check to the constructor of the service, and call _ = WebApplication.Services.GetService<MyService>(); after startup, just to force the service to be initialized early. However, this feels wrong again, since the outside world should not know that my service has special preconditions.

What I am looking for is a method in the service like CheckPreconditions() which is automatically called for each registered service, if it exists. When the preconditions check fails, the method throws an exception. Is there something like that, maybe a 3rd party library? Or is there another "clean" solution?


Solution

  • After some experiments, I came up with the following solution.

    The service with preconditions contains an inner class, including the assertion of the precondition. This inner class is required by the constructor of the service. Example:

    public class MyService {
    
       public MyService(Preconditions _, ... /* other dependencies */) {
          // Preconditions is just an unused parameter
       }
    
       public class Preconditions {
          public Preconditions() {
             if (myTestsFail)
                throw new Exception($"Preconditions of {nameof(MyService)} failed")
          }
       }
    }
    

    This way, checking the preconditions becomes a required dependency of the service. Code in Program.cs:

    services.AddSingleton(new MyService.Preconditions());
    services.AddSingleton<MyService>();
    

    If I accidentally forget to check the preconditions, the program will fail early. If the preconditions are not met, the program will also fail early.