Search code examples
c#asp.netowinsimple-injector.net-4.8

Simple Injector creating instances of controllers on calling container.Verify()


I'm using Simple Injector with Owin Selfhosted, but I'm having a problem on calling container.Verify().

When I call container.Verify(), it is creating an instance of all my controllers, but in some of my controller I have some rules, and depending on the configuration it throws an error saying that you can't use the route. Based on that, I can only create an instance of my controller when the endpoint is called.

Here is my startup code:

public void Configuration(IAppBuilder app)
{
    var config = new HttpConfiguration();
    
    Monitor.VerifyConfingurations();
    ManipulateHttpConfiuration(config);
    app.UseWebApi(config);
    var container = ContainerGerenciador.NovoContainer();
    ConfigureDI(container, config);
    if (debug)
    {
        config.EnableSwagger(c => {
            
            c.SingleApiVersion("v1", "Swagger WSLinear API");
            c.IncludeXmlComments("WSLinearApi.XML");
            c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
            c.UseFullTypeNameInSchemaIds();
        }).EnableSwaggerUi(x=> 
        {
            x.DisableValidator();
        });
    }
    var options = new FileServerOptions
    {                
        EnableDefaultFiles = false,
        DefaultFilesOptions = { DefaultFileNames = { "index.html" } },
        StaticFileOptions = { ContentTypeProvider = new CustomContentTypeProvider() }
    };
    app.UseFileServer(options);
}

private void ConfigureDI(Container container, HttpConfiguration config) 
{
    #region Register
    container.Register<ILinearLogger>(
    () => new Logger(new LinearLoggerConfiguration()
    {
        Path = PathFinder.GetLogsFilePath()
        
    }),Lifestyle.Transient);
    container.Register<IMyService,MyService>();
    Integrations.Setup.Register(container);
    #endregion
    container.RegisterWebApiControllers(config);
    config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
    container.Verify();//here the error is thrown
}

Controller Example:

[RoutePrefix("api/SampleIntegration")]
[SwaggerResponse(HttpStatusCode.BadRequest, "Business Logic error", typeof(BusinessExceptionResponse))]
[SwaggerResponse(422, "Invalid Entity", typeof(CamposInvalidosExceptionResponse))]
public class SampleIntegrationController : ApiController
{
    private readonly IMyService _myService;
    public SampleIntegrationController(IMyService myservice) 
    {
     _myService = myservice;
     if(!_myService.IntegrationEnabled())
        throw new BusinessException("Sample Integration is not enabled! Enable it to access the route.")
        //above the error is thrown because simpleInjector creates an instance of this controller before the route is accessed
        //but I only this exception to throws only when the route is accessed (if the integration is not enabled)
    }

    [HttpGet]
    public HttpResponseMessage Get()
       => CreateResponse(() => _myService.GetData());


        //...
    }

I've tried to remove the container.Verify() line, but it throws the same error when the first endpoint is called (It creates instances of all others controllers).

What can I do to make that Simple Injector doesn't create an instance of my controllers until it is called from the actual route?


Solution

  • With the introduction of version 5, Simple Injector automatically verifies your object graphs, even if you don't call Verify explicitly. This feature is called auto verification, and can be turned off. What you can do is move verification to a unit or integration test as described here in the Simple Injector documentation.

    Although this is a possible solution, I think you should consider a different validation strategy for your controllers.

    Having this logic inside your constructor causes difficulties, as you already noticed, which includes:

    • Difficulties in allowing Simple Injector to verify your configured object graphs as Simple Injector requires your components to be constructed in order to do reliable verification
    • Using dependencies from inside constructors makes object construction slow and unreliable, while we should strive the opposite.

    Instead, consider the following alternatives:

    • If the use of certain controllers is only allows on a certain configuration that is loaded (and fixed) after loading the application, consider filtering those controllers out and prevent them from being registered, rather than registering them and preventing them from being called.
    • Use an interception mechanism where a call to, for instance, the controller is intercepted. With Web API you can, for instance, you can apply an ActionFilter or DelegatingHandler that goes off before each called controller action. This keeps the validation logic out of the controller and allows the validation to be done after the object graph is constructed.
    • Intercept at the point where the controller calls its services. For instance, you can create a decorator for IMyService that ensures that the validation is triggered when IMyService is called. This solution is likely less convenient if controllers call a myriad of services, as you would be implementing decorators for all of them. When your controllers, on the other hand, only depend on a few generic interfaces, such as command handlers and query handlers, you would only have to implement one controller per generic interface, which might make this solution more feasible.