IoC and Custom Resolvers

33 views
Skip to first unread message

Barry Dahlberg

unread,
Oct 21, 2009, 1:57:52 AM10/21/09
to OpenRasta
I had this as a reply to the StructureMap post but it got out of hand
so I'm putting it in a new thread. For reference I'm refering to
these:

http://groups.google.com/group/openrasta/browse_thread/thread/ca62448433c246b9
http://groups.google.com/group/openrasta/browse_thread/thread/9bd55bc7d4369c69

These are my thoughts after making a short attempt as Unity support
this morning. Getting support for different containers right is hard
because your choice of container tends to imply certain things about
the design of your objects. For Unity the main issues seems to be:

* Constructor selection is different. Extension required.
* Propety injection is different. Extension required.
* Unity has no support for querying registered types by default.
Extension required.

For me I want a custom container because I already have significant
infrastructure based on it across ASP.Net, Win Forms, WPF and console
applications. My choice of container is based on many things
including the style of injection etc that I like. My objects are
designed around this and wont change for OR.

This means I only want to use those extensions when creating OR
objects, not my objects, and we run into the sub-container problems
again. I feel a bit like OR has things backwards because it is trying
to define how IoC is going to operate across the whole application
inluding user types. Ideally OR should not imply any restrictions on
the design of objects I provide so long as they meet the interfaces
specified.

What I really want is for OR to provide a default container which is
always used to construct default implementations of OR objects, even
if a custom container is registered. I want the users container to
always be asked for instances first, and to delegate back to the
default container when it fails. This would have a bunch of
interesting properties:

* OR's default objects are built according to the OR's IoC.
* User provided objects are designed as the user sees fit for their
container.
* Custom resolvers no longer have to provide AddDependency etc for
OR's benefit and just become Resolve*.
* Custom resolvers could be just simple factories if the user wanted
as they don't need to support OR's objects.
* Users can override default implementations (ILogger etc) just by
adding them to their own container and not worrying about ordering
issues.

The default container would need to call the users container for any
nested dependencies as well so that default implementations can use
user provided overrides. Some care would need to be taken to prevent
infinite loops. Most of this could be provided in an abstract
adapter.

After writing that out I'm left with a few questions... Is it
doable? Is it sensible? Am I crazy?

Barry

Barry Dahlberg

unread,
Oct 21, 2009, 2:08:49 AM10/21/09
to OpenRasta
To summarize, perhaps OR's IoC should be considered an extension of the users container, used for types the user hasn't provided.

2009/10/21 Barry Dahlberg <barry.d...@gmail.com>

Ryan Riley

unread,
Oct 21, 2009, 7:39:08 AM10/21/09
to open...@googlegroups.com
On Wed, Oct 21, 2009 at 12:57 AM, Barry Dahlberg <barry.d...@gmail.com> wrote:
For me I want a custom container because I already have significant
infrastructure based on it across ASP.Net, Win Forms, WPF and console
applications.  My choice of container is based on many things
including the style of injection etc that I like.  My objects are
designed around this and wont change for OR.

This means I only want to use those extensions when creating OR
objects, not my objects, and we run into the sub-container problems
again.  I feel a bit like OR has things backwards because it is trying
to define how IoC is going to operate across the whole application
inluding user types.  Ideally OR should not imply any restrictions on
the design of objects I provide so long as they meet the interfaces
specified.

What I really want is for OR to provide a default container which is
always used to construct default implementations of OR objects, even
if a custom container is registered.  I want the users container to
always be asked for instances first, and to delegate back to the
default container when it fails.  This would have a bunch of
interesting properties:

I don't know if this answers your question, but you don't have to hook Unity into the flow. If I recall correctly, Seb mentioned that OR's default container really isn't meant for everything for which we typically use a container. You can still use Unity alongside the default container. I can't remember what the advantages of using a specific container are aside from being able to reuse the core container for all IOC tasks.

Seb or Aaron, please correct me if I'm wrong here.


Ryan Riley

Email: ryan....@panesofglass.org
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Website: http://panesofglass.org/
 

Aaron Lerch

unread,
Oct 21, 2009, 7:43:26 AM10/21/09
to open...@googlegroups.com
I have a few thoughts on the larger questions (and I'm sure Seb will correct me) which I'll share in a bit, but to answer this specific question, the main reason for using a container other than the default (custom) container shipped by OR is for mixing types together. Injecting app services into OR handlers, or injecting OR components (IRequest, for ex) into your app services. To support this, your application container needs to know how to resolve OR types, and OR needs to know how to resolve your service types.
--
Aaron
http://www.aaronlerch.com/blog/

Aaron Lerch

unread,
Oct 21, 2009, 7:59:45 AM10/21/09
to open...@googlegroups.com
You seem to have a pretty good handle on the issues involved with integrating another container into OR.

One problem area that comes to mind is implementing a "per request" scope/lifestyle/lifecycle. This is still custom work in "your" container (the one resolving "user types"), because OR is abstracted away from the hosting environment and a request-specific cache is specific per environment.

In terms of whether it's "do-able" or not, what you describe is actually almost exactly what I implemented with the support for Ninject. You can use 2 kernels and the OR kernel (which does things the way OR expects) will fall back and resolve a type out of the "parent" kernel if it can't find one. The parent kernel can be configured however your application needs it to be. I do mostly like the idea of baking in this sort of support to OR itself to aid scenarios that aren't as easy to implement as Ninject. But again, the down side is that by doing it this way you lose the ability to mix your application types and OR's types together. You are basically forcefully limited to a one-way resolution - OR can resolve your application types, but your application can't resolve OR's types. So for example, an OR handler can get an instance of an application service (as it should be able to) but an application service in your app can't get a reference to the current request.

In conversations with Seb in the past, instead of forcing the hard restriction of 1-way resolutions on people, we instead chose to say: to have 2-way resolutions, your app has to use the configuration options OR expects (ctor selection, property injection, etc). If you don't want to do that, or can't do that, then you can have 1-way resolutions by having 2 container "repositories".

But I'm sure Seb has more thoughts on the issue :)

Barry Dahlberg

unread,
Oct 21, 2009, 9:13:36 AM10/21/09
to OpenRasta
Thanks for spending the time to help me work this out in my head. For
now I'm mostly going to respond with "Hmmm" and see what falls out as
I keep building my app. A couple of of thoughts for now though:

The per request storage actually doesn't bother me too much as I've
done similar things for ASP.Net before. Trying to match the injection
behaviour is the tricker part with Unity I think.

You say "...an OR handler...". I tend to think that anything I am
writing is mine, not OR's, and I want it to be as consistant as
possible with the rest of my application. This includes handlers,
pipeline contributors etc, hence the resistance to OR's assumptions
about constructor selection etc. Sounds a bit selfish really, perhaps
I just need to get over it.

An application service that wants to get hold of OR's objects is
possibly layered a bit backwards anyway?

Barry
> On Wed, Oct 21, 2009 at 1:57 AM, Barry Dahlberg <barry.dahlb...@gmail.com>wrote:
>
>
>
>
>
> > I had this as a reply to the StructureMap post but it got out of hand
> > so I'm putting it in a new thread.  For reference I'm refering to
> > these:
>
> >http://groups.google.com/group/openrasta/browse_thread/thread/ca62448...
>
> >http://groups.google.com/group/openrasta/browse_thread/thread/9bd55bc...

Barry Dahlberg

unread,
Oct 21, 2009, 9:26:34 AM10/21/09
to OpenRasta
"You can use 2 kernels and the OR kernel (which does things the way OR
expects) will fall back and resolve a type out of the "parent" kernel
if it can't find one."

I'm having trouble finding the behaviour you've described in the code,
could you point me in the right direction please?

Barry

Aaron Lerch

unread,
Oct 21, 2009, 9:30:39 AM10/21/09
to open...@googlegroups.com
Sure, here's the "subcontainer" implementation (it's a standard kernel that looks at a parent IKernel if it can't resolve a type)

And this class has a static method to create and configure the kernel (CreateKernel)

Barry Dahlberg

unread,
Oct 21, 2009, 9:59:34 AM10/21/09
to open...@googlegroups.com
Cheers, had missed the second version of CreateKernel.  Silly question to be asking after all that but where do you actually set NInject as the resolver?  For AspNet at least it looks like you implement IDependencyResolverAccessor somewhere and it's magically found by the framework, correct?


2009/10/21 Aaron Lerch <aaron...@gmail.com>

Aaron Lerch

unread,
Oct 21, 2009, 10:13:24 AM10/21/09
to open...@googlegroups.com
Correct-ish :) you do implement IDependencyResolverAccessor, and then you inform the host about it (so it's not quite so much magic). In the case of the HttpListenerHost, you give it the type that implements IDependencyResolverAccessor, and it creates an instance and uses it. I'm not sure what the AspNet hosting does, as I haven't used it.
--
Aaron
http://www.aaronlerch.com/blog/

Barry Dahlberg

unread,
Oct 21, 2009, 12:45:43 PM10/21/09
to OpenRasta
My notes after delving more into Unity with regard to the OR tests
provides:

Unity has child containers which should work nicely as was talked
about. I need to verify this but I think the implementation also
makes the "app services can't depend on OR types" problem go away.
This thread shows an example of a type registered in the parent
container receiving dependencies from the child:

http://unity.codeplex.com/Thread/View.aspx?ThreadId=46415

This only happens when the resolve request is made through the child
but this covers all the use cases that make sense to me anyway.
Extensions are required to modify the following:
- Constructor selection. Constructor with most parameters used by
default, even if they can't be resolved, dumb grr.
- Property injection. No injection if not marked with attribute by
default.

ResolveAll only returns types registered with a name, not default
types. Is asking users to register everything with a name
reasonable? Probably not. It could be used to implement
HasDependency etc as well as these are not provided any other way. If
all else fails another extension could be added. This should be
standard, grr.

The circular reference test provides a nice way to close NUnit in a
hurry and Unity helpfully documents the solution as "It is the
responsibility of the developer to prevent this type of error by
ensuring that the members of classes they use with dependency
injection do not contain circular references.". Unless OR plans to
try registering circular dependencies is there any reason to enforce
this on all containers? Users choosing Unity should be familiar with
that behaviour, dumb as it is. Grr.

3 grrs, Unity is not doing itself any favours here.

Barry

Sebastien Lambla

unread,
Oct 21, 2009, 5:17:10 PM10/21/09
to open...@googlegroups.com
Sorry, been away from the computer all day.
 
Yes, OR puts more constraints on how a resolver is supposed to work. I don't like the one-way resolution of most other frameworks, as I believe it is normal behavior for a handler to want to depend on IReqest, IUriResolver or any other service provided by OR.
 
Anyway, on to the actual questions.
 
> Child containers...
 
Indeed, what you register above OR should probably not be dependent on OR components. But care needs to be taken to choose which contianer you do your registration on, should you wish to register components called by OR.

 
> ResolveAll only returns types registered with a name, not default
> types. Is asking users to register everything with a name
> reasonable? Probably not. It could be used to implement
> HasDependency etc as well as these are not provided any other way. If
> all else fails another extension could be added. This should be
> standard, grr.
 
Well, I see no reason why you couldn't always register all types using the type name of the service, so OR components get registered the "proper way", and if users want to use OR extensibility points in the container rather than through the config, then you're free to impose this fact on users of the unity container.


> The circular reference test provides a nice way to close NUnit in a
> hurry and Unity helpfully documents the solution as "It is the
> responsibility of the developer to prevent this type of error by
> ensuring that the members of classes they use with dependency
> injection do not contain circular references.". Unless OR plans to
> try registering circular dependencies is there any reason to enforce
> this on all containers? Users choosing Unity should be familiar with
> that behaviour, dumb as it is. Grr.
 
Cyclic references are a nightmare. Enforcing the behavior reduces the risk of users coming to this mailing list asking about why their registration fails.
 
That said, as the default behavior is really intended for stuff OR call, that shouldnt be preventing people from doing those registrations on the root container.

 
> 3 grrs, Unity is not doing itself any favours here.
>
> Barry

Thanks a lot for the effort in adding Unity. Thanks to you guys we may well have support for all major containers in time for RTM. Exciting!
 
If you're going to contribute the code back, don't forget to sign the contributor agreement and send it back :)
 
Seb


Did you know you can get Messenger on your mobile? Learn more.

Barry Dahlberg

unread,
Oct 21, 2009, 8:43:26 PM10/21/09
to OpenRasta
Will see how far I get with Unity. Signing the agreement is probably
the biggest problem as I'm on a laptop in Japan and have no printer,
whoops, do you accept photoshop signatures?

:p
> _________________________________________________________________
> Did you know you can get Messenger on your mobile?http://clk.atdmt.com/UKM/go/174426567/direct/01/

Sebastien Lambla

unread,
Oct 22, 2009, 2:28:36 AM10/22/09
to open...@googlegroups.com
If you write down your agreement by email, I can give you commit access, provided I get the hard copies at some point :)
 
> Date: Wed, 21 Oct 2009 17:43:26 -0700
> Subject: [openrasta] Re: IoC and Custom Resolvers
> From: barry.d...@gmail.com
> To: open...@googlegroups.com

Chat to your friends for free on selected mobiles. Learn more.

Barry Dahlberg

unread,
Oct 23, 2009, 1:54:15 AM10/23/09
to OpenRasta
Figured I would submit a patch as described here:

http://trac.caffeine-it.com/openrasta

Have created a ticket but seem unable to edit it to amend it or add
the patch, doh. Should those docs be updated?

Barry

On Oct 22, 3:28 pm, Sebastien Lambla <s...@serialseb.com> wrote:
> If you write down your agreement by email, I can give you commit access, provided I get the hard copies at some point :)
>
>
>
> > Date: Wed, 21 Oct 2009 17:43:26 -0700
> > Subject: [openrasta] Re: IoC and Custom Resolvers
> > From: barry.dahlb...@gmail.com
> _________________________________________________________________
> Chat to your friends for free on selected mobileshttp://clk.atdmt.com/UKM/go/174426567/direct/01/

Sebastien Lambla

unread,
Oct 23, 2009, 4:17:04 AM10/23/09
to open...@googlegroups.com
I've relaxed the security to both ticket edition and the wiki edition, as it
seems the spam filter now works sufficiently well.

Seb

Barry Dahlberg

unread,
Oct 23, 2009, 4:59:00 AM10/23/09
to OpenRasta
Cheers. Tried at ammend my ticket, comment rejected as spam, oh well.

Submitted my changes to the trunk, probabl should have asked first but
was that the right place? Bunch of notes to follow.

Barry
:)

Barry Dahlberg

unread,
Oct 23, 2009, 5:11:07 AM10/23/09
to OpenRasta
These are my notes on what I've done, some of which should probably
become part of the documentation at some point. Any feedback welcome
and I would suggest a quick code review to make sure it's all sane.


BUILD

Built against the official Unity 1.2 release.

I haven't modified anything existing, just added the lib/unity-1.2 and
src/unity directories. I've made things work with little regard for
performance, it probably needs some more realistic usage to see if
this is an issue.

All tests pass including another 20 or so I added to confirm the
details of the Unity implementation. I got all the default tests to
pass long before OpenRasta would server my basic sample so you might
want to consider moving some of these into the core. Many are
specific to Unity oddities though.

I've only been building directly from VS 2008 and I haven't looked in
detail at what's needed to support various versions of .Net etc.


SUB CONTAINERS

Sub container functionality is provided. Application services
registered in the root container can depend on OpenRasta classes so
long as the Resolve call originates from inside OpenRasta. Types
registered in the root container use the default Unity injection
policies, types registered in the sub container use OpenRasta
constructor and property injection style.

Read that last sentance again because it relates to what this thread
was originally about. Application and OpenRasta components can be
resolved from the same container with different injection strategies.
Weird, but it's one thing Unity made reasonably straight forward to
do.

Per request lifetimes are implemented as another child container which
is created and disposed as needed. The container is put into IContext
rather than all the individual instances, this was Unity should
Dispose things for you each request.

Extensions such as adding interceptors etc can be added to your parent
container and should work for all types including the OpenRasta ones.
If you are not using a parent container then the resolver exposes its
internal one for you to play with too.


RESTRICTIONS

There are 3 things users must know to use this...

First, to get the subcontainer functionality to work you need to
install the TypeTracker extension in your root container, that's the
least disruptive way I could find to do it.

Second you must register all required dependencies, even for types in
your root container. This differs from Unity's default behaviour. I
couldn't find a sensible scheme for removing this restriction on the
root container as it would always conflict with OpenRastas
requirements.

Third, named dependencies are ignored by the TypeTracker extension and
hence basically don't exist to OpenRasta. I don't use named
components myself so I wasn't sure what their behaviour should be in
various cases. You could probably make this work if you wanted but it
might be non-trivial.


CONCERNS

The circular dependency tests pass, but I'm not overly confident in my
implementation. It assumes some things about the way ObjectBuilder
works and I'm not sure if they are true or not. We may find it is a
little aggresive about rejecting things.

I'm not an expert on Unity so there may be better ways to do things,
would love someone to tell me if there is!


SAMPLE

I included a short sample I was using to test things. It shows:
- Application service registered in a root container.
- Handlers etc resolved from subcontainer.
- Overriding OpenRastas default ILogger.

It's worth discussing somewhere in the docs why you might want to use
a custom container. Given OpenRastas restrictions there are two
interesting scenarios to me:

a) You have existing infrastructure you want to leverage.
b) You want to use advanced features of a container such proxy
generation.

There is little point in using Unity etc in place of the default and
then not doing anything interesting with it.


TODO

Things I haven't done, probably better for Seb to do:
- Integrated into the proper build.
- License information in the files.

There are two minor bits of code adapted from the Unity codebase
because they are private but more or less required. You might want to
check the licensing though I don't think they are a concern. See
ConstructorSelectorPolicy and PropertyInjectionPolicy for details.

I also haven't used the functionality in any meaningful way yet so no
gauranteees. I will be integrating it into a live codebase shortly to
see how it goes though.

I will probably blog some thoughts on Unity at some point, to be
honest I'm thinking about switching containers...

Sebastien Lambla

unread,
Oct 23, 2009, 6:36:05 AM10/23/09
to open...@googlegroups.com
Unity is using MS-PL. I only allow code licensed as MIT or something
compatible (aka not more restrictive than it, such as FreeBSD).

So I'm afraid those two classes need to be removed from the codebase and
rewritten to achieve the same result without reusing the same code or logic.

:(

-----Original Message-----
From: open...@googlegroups.com [mailto:open...@googlegroups.com] On
Behalf Of Barry Dahlberg
Sent: 23 October 2009 10:11
To: OpenRasta
Subject: [openrasta] Re: IoC and Custom Resolvers

Ryan Riley

unread,
Oct 23, 2009, 9:26:17 AM10/23/09
to open...@googlegroups.com
Sounds like this would fit well into the plugin infrastructure.

Sent from my iPhone

On Oct 23, 2009, at 5:36 AM, "Sebastien Lambla" <s...@serialseb.com>
wrote:

Sebastien Lambla

unread,
Oct 23, 2009, 9:46:55 AM10/23/09
to open...@googlegroups.com
Yes, it would woudln't it.

That said, I'd much prefer any "official" code, future plugin or not, to
remain MIT. For other licenses I'd recommend living out of the OR main
release.

Seb

Barry Dahlberg

unread,
Oct 23, 2009, 9:54:35 AM10/23/09
to OpenRasta
The MIT license is one of my favourites, it pretty much says "Hey, no
worries!". Unfortunately it can make things like this a bit tricky.

Anyway, I had a poke around and came up with a different way of doing
things which Unity probably didn't expect but hey, it works. I've
tried to preserve as much of the original Unity behaviour as possible
which is good, but means when 2.0 comes out the upgrade might not be
so straight forward.

Barry

Sebastien Lambla

unread,
Oct 27, 2009, 6:32:45 AM10/27/09
to open...@googlegroups.com
Thanks. It's all brilliant news and great work. We'll include support in the
RTM.

Seb

-----Original Message-----
From: open...@googlegroups.com [mailto:open...@googlegroups.com] On
Behalf Of Barry Dahlberg
Sent: 23 October 2009 14:55
To: OpenRasta
Subject: [openrasta] Re: IoC and Custom Resolvers

Reply all
Reply to author
Forward
0 new messages