Note that there are some explanatory texts on larger screens.

plurals
  1. PODynamic dependencies in deep object graphs - what am I doing wrong?
    text
    copied!<p>I'm trying to modify some existing code to use Castle Windsor as an IoC container. </p> <p>The application in question is document-oriented. So it has an object graph that relies on data for specifying the data source that can't be known at registration time, and will change every time the object graph is resolved. However, this dependency is a few layers into the object graph, so I can't just pass it as an argument to <code>container.Resolve()</code> since Windsor doesn't propagate inline dependencies.</p> <p>The solution I've come up with instead is to use the typed factory facility to generate a factory which can resolve every dependency in the graph, and have <code>Document</code>'s constructor take an instance of this factory as well as a string specifying the data source. The factory is specified at registration time, and the data source is specified at resolution time. The constructor takes over from there, manually polling the factory to resolve is dependencies. The result looks something like this:</p> <pre><code>public interface IDataSource { /* . . . */ } public interface IWidgetRepository { /* . . . */ } public interface IWhatsitRepository { /* . . . */ } // Implementation generated by Windsor's typed factory facility public interface IFactory { IDataSource CreateDataSource(string path); IWidgetRepository CreateWidgetRepository(IDataSource dataSource); IWhatsitRepository CreateWhatsitRepository(IDataSource dataSource); void Release(IDataSource dataSource); void Release(IWidgetRepository widgetRepository); void Release(IWhatsitRepository whatsitRepository); } public class Document { private readonly IDataSource _dataSource; private readonly IWidgetRepository _widgetRepository; private readonly IWhatsitRepository _whatsitRepository; public Document (IFactory factory, string dataSourcePath) { _dataSource = factory.CreateDataSource(dataSourcePath); _widgetRepository = factory.CreateWidgetRepository(_dataSource); _whatsitRepository = factory.CreateWhatsitRepository(_dataSource); } } </code></pre> <p>This absolutely works, and does accomplish the goal of having Windsor take charge of dependency resolution. Or at least, I can easily reconfigure the application by modifying the registration code. At the moment I'm still making one call to <code>container.Resolve()</code> for every <code>Document</code> that gets created, but I believe that sin can easily be amended with a second Typed Factory.</p> <p>However, it still feels wrong. Windsor is in charge of newing up objects and (somewhat) managing their lifetimes, sure. But it's not really injecting those dependencies into <code>Document</code>'s constructor; instead the constructor is pulling them out of the factory. Worse, by passing the instance of <code>IDataSource</code> into the factory methods, it's taken charge of managing the object graph. That seems like a huge subversion of the inversion to me.</p> <p>So, what am I missing?</p> <p><strong>EDIT</strong></p> <p>To clarify, what I think I'm supposed to be shooting for is for <code>Document</code>'s constructor to look more like the below.</p> <pre><code>public Document (IDataSource dataSource, IWidgetRepository widgetRepository, IWhatsitRepository whatsitRepository) { _dataSource = dataSource; _widgetRepository = widgetRepository; _whatsitRepository = whatsitRepository; } </code></pre> <p>That way Windsor is in direct control of supplying all the objects' dependencies using constructor injection. That's actually what the constructor signature looks like in the original code, but I was unable to get it to work with Windsor because <code>container.Resolve()</code> <a href="http://docs.castleproject.org/Windsor.Passing-Arguments.ashx" rel="nofollow noreferrer">doesn't propagate inline dependency parameters</a>. So I can't just do:</p> <pre><code>var document = container.Resolve&lt;IDocument&gt;(new { dataSourcePath = somePath }); // throws an exception </code></pre> <p>because Windsor doesn't pass <code>dataSourcePath</code> on to <code>DataSource</code>'s constructor, much less make sure that a <code>DataSource</code> instantiated with that path is passed on to the other constructors. </p> <p><a href="https://stackoverflow.com/a/3905496/516797">An answer to another question</a> points out that this is by design - doing otherwise would introduce coupling. This is a no-no, since one shouldn't mandate or assume that implementations of an interface have particular dependencies. Sadly, that points out another way I think the code I came up with is wrong, since <code>Factory.CreateWidgetRepository()</code> and <code>Factory.CreateWhatsitRepository()</code> imply just that sort of assumption.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload