LightInject
LightInject copied to clipboard
Issue with scope in WebAPI on IIS
I have a following problem: In global.asax inside Application_BeginRequest I'm initializing my database context after createing a ScopeManager and storing it in the request's context (copied from LightInject.Web, because I can't use the LightInjectHttpModule approach as the BeginRequest and EndRequest are executed in different order as I require and I need it predictable). Then inside Application_EndRequest I tear it down. The problem is, that after the Application_BeginRequest (and all the associated events) the dependency resolver for WebAPI is initialized and a scope for WebAPIS' dependency resoluton is requested from LightInjectWebApiDependencyResolver.BeginScope and this creates a new child scope that won't contain my already initialized instance of the database context and I get an uninitialized new instance injected into my controllers. I was thinking about using and action filter to initialize the database context but they are executed after any authentication filter(and I'm using them and require the database context already initialized inside them) and I just don't want to initialize the database context in any authentication filter. Am I missing some usage pattern or do I need to need to hook up LightInject into WebAPI with my custom code (I will do it for now as I already killed more few hours trying to figure out why I suddenly get a new instance of the database context and I need to move on with the work)?
One idea that just occurred to me would be to use create a lifetime that would search for root scope and use that as the key to store the instance in a lifetime similar to PerScopeLifetime.
Hi.
I am not sure what you mean when you say that the BeginRequest and EndRequest event are executed in a non-deterministic order, but it might be related to redirects where you can have a EndRequest without a corresponding BeginRequest. LightInject.Web 1.0.0.6 takes care of this in the EndRequest handler and will not throw an exception in the case where there is no BeginRequest.
If you are on .Net 4.5 or higher, you might try to just rely on the PerLogicalCallContextScopeManagerProvider to handle the scope for you. IMHO, I think you should think twice about trying to handle this yourself as it quickly becomes incredibly easy to mess things up.
container.ScopeManagerProvider = new PerLogicalCallContextScopeManagerProvider();
Best regards Bernhard Richter
Bernhard, thank you for your answer, but I think you got my problem a little bit wrong, maybe I didn't describe it clearly. So there are 2 problems: 1st problem - LightInjectHttpModule This HTTP module registers event handlers for BeginRequest and EndRequest of the application. I also register handlers for those events (either as event handlers or as Application_BeginRequest/Application_EndRequest). The problem is the execution order of the handlers. As I need to do some stuff in BeginRequest to initialize my DB context and then tear it down in EndRequest. First of all, I can never rely on the order of handler execution being same as the order they were attached, generally it works, but there is nowhere specified that it will always work. Second, even if I could and the LightInjectHttpModule event handlers are execute after those defined in Global.asax, there is a problem with getting an per scope instance of the DB context from container as there the scope isn't yet initialized. And then if the event handlers attached in LightInjectHttpModule get executed before the event handlers from Global.asax, then I have problem with the code in EndRequest handler from Global.asax as I can't get the instance of the DB context from container as it is already disposed. I need to insert the execution of my BeginRequest nad EndRequest event handlers between those attached in LightInjectHttpModule. Maybe I could do my attaching of BeginRequest handler in Application_Start and the EndRequest in Init() and this could work, I have to test it, but still, I don't think it is a nice solution.
2nd problem - LightInjectWebApiDependencyResolver Let's say I have the 1st problem solved, then I have a second problem. LightInjectHttpModule starts a scope for the request and then I retrieve an instance of the DB context with per scope lifetime in my BeginRequest handler and do the intialization magic of the context. Then the pipeline moves to resolution of the called API controller and LightInjectWebApiDependencyResolver kicks in and in BeginScope it starts a new scope of the container. So I did my stuff inside BeginRequest for nothing, because new instance of my DB context gets injected into the controller, not the one already initialized and stored in the parent scope.
Hi again. I now understand what you mean with regards to the BeginRequest/EndRequest handlers. Is this some implementation of the "unit of work" pattern? You initialize the DB context (Entity framework?) in the BeginRequest and depending on the state of the context you perform a commit or rollback?. I am just trying to understand what the actual problem here :) Maybe there are other ways to achieve this that does not require an extra set of request handlers.
I'm using AsyncPoco (async/await supporting version of PetaPoco) and built a custom database context holder around it. And yes, I'm implementing an unit-of-work pattern with initialization in BeginRequest and Rollback in Error event handler of application and Commit in EndRequest. I have it registerd with per scope lifetime and need to have it initialized before the pipeline gets to the execution of authentication filters (BeginRequest being only logical option) and have it accessible also for injection into the controllers and for the commit in EndRequest.