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?
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:
Instead, consider the following alternatives:
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.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.