IContextStore and IContextStoreDependencyCleaner

7 views
Skip to first unread message

Aaron Lerch

unread,
Aug 17, 2009, 12:46:12 AM8/17/09
to OpenRasta
I'm implementing Ninject support for OpenRasta (because I want to use
OR in our app which is using Ninject, not because I like pain ;) ).
Things are pretty straightforward so far (I'll post the code when it's
complete) but I have some questions because I want to verify my
assumptions. I'm using the Windsor implementation as a reference.

I think my questions all boil down to whether this is true or false
(or somewhere in between):
I am responsible for taking instances created with the "PerRequest"
lifetime and placing them into the IContextStore (for which I'm
assuming OR adds the dependency so I can just grab it out of Ninject
like anything else) and when the "cleaner" for that instance is
invoked, I should remove the instance from Ninject and any future
requests for it should create a new instance.

Ninject uses the garbage collector to control object lifetimes (using
a "scoped object"), so I'm just being very careful to make sure I
implement this correctly. The Windsor implementation has a lot of code
that I'm not familiar with, since I haven't used Windsor before, so
I'm having to pick it apart to discover what is Windsor, what is OR,
and what is the glue between them. :)

Thanks!

Sebastien Lambla

unread,
Aug 17, 2009, 3:23:38 AM8/17/09
to open...@googlegroups.com
Hey Aaron,

the cleaner interface is there to make sure that the custom lifetime manager can do whatever cleanup is required when the object is not neeeded anymore. In the case of windsor, it's for example to ensure Dispose() is called on all the IDisposable objects that are not needed anymore.

The implementation of IContextStore is host-dependent. It uses HttpContext.Current.Items in the asp.net hosting, and in HttpListener it stores it in the CallContext. And you're absolutely correct, the host is supposed to register that type before anything else, as a singleton.

That means on the one hand that you shouldn't have to care about removing the object instances from IContextStore. But it can't hurt either, so maybe I should add some tests in the DI tests to make sure we do it, or make sure it's done from within the hosting API. Not sure.

For those of you that haven't delved into that code, the reason for the custom lifetime management existing is two-fold.

First, we can't rely on the PerRequest in most containers, as those are tightly coupled to the HttpContext. This would require, when you take a project and republish it in another host, to reconfigure your container.

Second, OR is involving itself quite heavily in object creation. There are a bunch of object instances that get injected in the container for the length of the Request, so that dependency injection can happen everywhere else.

I'm very happy to see a third container supported. Now only StructureMap, Unity and Autofac are needed and we'll be done for RTM! :)

Seb

> Date: Sun, 16 Aug 2009 21:46:12 -0700
> Subject: [openrasta] IContextStore and IContextStoreDependencyCleaner
> From: aaron...@gmail.com
> To: open...@googlegroups.com

Internet Explorer 8 - accelerate your Hotmail. Download Internet Explorer 8

Aaron Lerch

unread,
Aug 18, 2009, 8:27:09 AM8/18/09
to open...@googlegroups.com
I've only had a bit of time to spend on it, but I think I'm nearly finished with the Ninject implementation.
A few "gotchas":

- Ninject (out of the box) won't inject into properties unless they have an attribute set on them (the attribute is easily customizable). You can customize Ninject, which I've done, to support injecting to any property that has setter for a "bound" type. So this works now.
- It looks like Windsor will only use a constructor for which it knows how to resolve all the arguments, but Ninject (again, out of the box) doesn't discriminate - it just tries to bind to the constructor with the most parameters. I've customized this as well so that it will "score" constructors based on the number of arguments it contains type bindings for.

Right now I'm left trying to debug why (when using Ninject) some OR internals don't seem to be behaving correctly and are throwing errors when configuring. I'll let you know when I've got that figured out.

Lastly, the main concern I have with Ninject support is that it seems concerning to me that in order to work with OR some default behavior needs to be changed. I suppose that's going to happen when you're trying to support multiple IoC frameworks, but in my scenario, for example, I'm integrating OR into a desktop application where we have pre-existing (though not pervasive) Ninject usage, and to have OR demand that our usage throughout our app change could be a deal breaker.
Fortunately for me it's not, but I'm just thinking of other similar scenarios where the "encapsulating" app has more investment in a framework.

Thanks!
Aaron

Sebastien Lambla

unread,
Aug 18, 2009, 8:46:25 AM8/18/09
to open...@googlegroups.com

Hey Aaron,

 

Yes, that’s a problem that having a container at the core will trigger. This is the same problem if I want to switch to supporting array types in constructors, as Windsor doesn’t support that by default. I’m not however entirely sure how we can solve this issue...

 

Maybe for those containers that support sub containers we could create a sub container when we associate with the container, and add our customizations there. I tried and failed with Windsor, but I know autofac has support for them. What about ninject?

 

In the end though, I don’t think there’s much we can do. If one wants to use their own container, it has to support a few features, which means customizing the container. If that’s not possible, then one could just live with two containers, and use some factory support in the destination container to pull objects from openrasta using the service locator. Maybe that’s something to consider as a potential avenue to think about?

 

Seb

Victor Kornov

unread,
Aug 18, 2009, 9:19:08 AM8/18/09
to open...@googlegroups.com
I thinks its a new ground for most if not all containers. When you group the code by features not layers those groups/areas/plugins can really come from different sources and have different requirements for IoC behaviour. Yes, subcontainers seems to be the answer, but it's not something easily achived all the same.

Btw, Seb whas the problem with Windsor, in short? AFAIK, Windsor's not the 1st level support for subcontainers (and app components) was the reason for Autofac.

On Tue, Aug 18, 2009 at 4:46 PM, Sebastien Lambla <s...@serialseb.com> wrote:

Hey Aaron,

 

Yes, that’s a problem that having a container at the core will trigger. This is the same problem if I want to switch to supporting array types in constructors, as Windsor doesn’t support that by default. I’m not however entirely sure how we can solve this issue...

Aaron Lerch

unread,
Aug 18, 2009, 10:06:39 AM8/18/09
to open...@googlegroups.com
Ninject doesn't have support for it out of the box (surprise!!) but I wonder if it could be done. Everything in Ninject is contained within an IKernel instance... and you can implement your own as well. I wonder if I could override the "StandardKernel" implementation to accept another kernel in it's constructor, and defer any resolutions the current one can't resolve to the composed IKernel. I think that would effectively let us customize a kernel without impacting a "parent" kernel.

I'll give it a go tonight or later this week. (I'll also post the question on the Ninject group.)

Sebastien Lambla

unread,
Aug 18, 2009, 12:01:44 PM8/18/09
to open...@googlegroups.com

The thing with the Windsor container is that subcontainers can look “up”, but once they’re up there they can’t look down.

 

There are two scenarios, and only one would work at all.

Scenario 1:

-          The fluent API registers the handler in the sub container

-          The handler depends on IRequest, so that resolves in the same sub container

-          All is good

Scenario 2:

-          The handler is registered in the root container

-          It depends on IRequest, the request is defined in the sub container

-          No good

Scenario 3:

-          The handler is registered in the sub container

-          The handler depends on a service the user registered in the root container

-          The service depends on something from openrasta, and as such in the sub container

-          No good.

 

 

All in all, I think there’s always going to be a problem with making this work equally across containers, but it’s a lesser than two evils, the other option being having one container for openrasta and one for the rest. I don’t believe it’s the right way to go.

 

My personal preference is that those problems are not major enough to be a problem for most, and if it is a problem, they have an option to use a different container and loose the ability to access components both ways (which is the evil case i don’t want to make the default :) )

Aaron Lerch

unread,
Aug 18, 2009, 12:28:35 PM8/18/09
to open...@googlegroups.com
For the Ninject support, I'll implement the Ninject container with *optional* support for a parent container (Scenario 3). But it'll be optional.
For my specific scenario I want to use OpenRasta for, I can easily live with a "one-way" resolution where my types can't access OpenRasta types unless I've defined my types in the same container. But that's just my specific scenario.

Sounds like what containers need (and what Ninject could probably be made to work with) is a graph of sibling containers with "path" detection (to avoid infinite resolution loops).
Reply all
Reply to author
Forward
0 new messages