Windsor, Decorators and Chains of Responsabilityes, Oh YEAH!

A few days ago I made a small change to Windsor, the change was basically stopping Windsor from trying to resolve a dependency using a dependency that it is already resolving. Now, this is prettry hard to explain without a good example, so consider this one:

image

Let us say that we have the standard Chain Of Responsability here, each finder will check its store for an item that matches the specification, and would call the next one in the chain if it can't. Now, previously you had to instruct Windsor explicitly about who was the next in the chain, something like this:

<components>
	<component id="cache_finder" service="IResultFinder`1"
		type="CacheResultFinder`1">
		<parameters>
			<finder>${db_finder}</finder>
		</parameters>
	</component>
	<component id="db_finder" service="IResultFinder`1"
		type="DatabaseResultFinder`1">
		<parameters>
			<finder>${ws_finder}</finder>
		</parameters>
	</component>
	<component id="ws_finder" service="IResultFinder`1"
		type="WebServiceResultFinder`1">
		<parameters>
			<finder>${failed_finder}</finder>
		</parameters>
	</component>
	<component id="failed_finder" service="IResultFinder`1"
		type="FailedResultFinder`1"/>
</components>

That is quite a bit of XML, and while it works, it make it awkward to understand what is going on in complex scenarios. The main issue here was that Windsor looked at CacheResultFinder<T>, saw that it accepted IResultFinder<T> and that it had a valid component that had this service (CacheResultFinder<T>) and promptly threw a dependency cycle exception. As a direct result of that, we had to manually override Windsor's selection, and also had to have a FailedResultFinder<T>, which must go on the end of the chain, again, because otherwise Windsor would try to resolve the ctor(IResultFinder<T> finder) constructor.

This is a workable solution, but I feel that this is telling the container way too much, why isn't it smart enough to figure it out? The problem with saying it about OSS software is that I can't really rant about it, since the answer is usually "we accept patches", bummer. So, I went ahead and implement this feature in Windsor, where it will not try to resolve what is currently being resolve. The end result is that for the scenario above we can give up on FailedResultFinder<T> and write just this:

  <components>
    <component id="cache_finder" service="IResultFinder`1"
      type="CacheResultFinder`1"/>
    <component id="db_finder" service="IResultFinder`1"
      type="DatabaseResultFinder`1"/>
    <component id="ws_finder" service="IResultFinder`1"
      type="WebServiceResultFinder`1"/>
  </components>

We put the responsability for putting the chain of responsability together in the hand of the container, and it will build the system using simple first come fist on the chain approach, until it runs out of components that can satisfy the requirement, in which case it will use the default constructor.

Now this is much better, no?

Print | posted on Monday, June 11, 2007 9:52 AM

Feedback


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 10:43 AM Alex Henderson

Love it, that's so much easier to work with then the previous approach :) is it in the trunk already?


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 10:58 AM Mark Monster

Hmm, sounds nice. But at the end, normally there is no ordering in XML-Elements is there? So the ordering is implicit. I think this will work, but maybe these chains should be grouped in a way that the chains can be found easyly.


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 11:45 AM Ayende Rahien

Alex,
of course :-D


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 11:46 AM Ayende Rahien

@Mark,
XML has _strict_ ordering.
I don't follow the rest.


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 12:07 PM Mark Monster

Hmm just to add. I like to document my solution. If the code/xml doesn't speak for itself I tend to add comments. What I do in the case of a responsability chain is.

<!-- Begin ABC chain -->
<component.../>
<!-- End ABC chain -->

I'm not sure if there can be found a better way to document the chain with name etc... I was thinking about something like this...
<componentChain>
<component.../>
</componentChain>

But now mentioning this, I don't think this solution is desirable.


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 12:10 PM Simone Busoli

That's interesting, Ayende. Could you explain a little better what happened with the former approach? You say that a dependency cycle exception was thrown, was it handled by Windsor or what?


Gravatar

# Spelling responsibility 6/11/2007 12:36 PM Damien Guard

http://www.answers.com/responsibility&r=67


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 2:01 PM Ayende Rahien

Simone,
It would throw, unless you would instruct it to seek a specific one, that is why you have all the <finder>${db_finder}</finder> elements.


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 2:41 PM Nate Kohari

I would agree that while this is a pretty clean answer, it seems like there should be more explicit indication that it's a chain -- either:

<chain>
<component .../>
<component .../>
</chain>

Or:

<component chain="A" .../>
<component chain="A" .../>

Otherwise, if your schema gets very complex, it might be difficult to discern which components are being resolved. For example, if you have:

<component service="IService" type="ServiceImpl1"/>
<component service="IAnotherService" type="FooImpl"/>
// ...another 50 component definitions...
<component service="IService" type="ServiceImpl2"/>

Wouldn't the second "chained" request for IService result in SerivceImpl2? This seems like it could be counter-intuitive...


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 3:09 PM Ayende Rahien

@Nate,
If you got to this point, you really should split the configuration to separate files, and you can always override the default behavior


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 3:18 PM Bill Poole

Great work! This is exactly what I was looking for!


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 4:08 PM Eric Nicholson

I've actually run into this exact problem in the past. The work around I used was to make the constructor for each of the components in the chain take a simple Object, rather than the Interface type, and then do a cast.

This solution looks great! I would prefer something a little more explicit though in case you needed to select a specific service implementation later. Maybe the <chain> syntax mentioned earlier. Or even, just use the first XML. In my experience Windsor would through a cycle dependency exception in that situation as well.

Great stuff!


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 7:41 PM Simone Busoli

As other observed I think that transparently providing this behavior might be misleading. I'd opt for a more explicit syntax like the one suggested by Nate.
BTW, do you know how other IoC containers deal with this?


Gravatar

# re: Windsor, Decorators and Chains of Responsabilityes, Oh YEAH! 6/11/2007 9:31 PM Ayende Rahien

@Simone,
This is optional, but I like the syntax. Nate's second suggestion is possible today.
About other IoC, no idea, frankly.

Comments have been closed on this topic.