Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I believe I've found the correct solution. Apparently the available documentation wasn't quite explicit (verbose?) enough to bang the concept through my thick skull the first twelve times I read it, so I'll make an attempt to document it in more detail here for the sake of others who might be as helpless as I am. (I'll also let it sit a while before accepting in the hope that someone else might come along with any other/better suggestions.)</p> <p>Long story short, Typed Factory Facility is the wrong tool for the job.</p> <p>--</p> <p>The trick is to use the DynamicParameters facility in the fluent registration API, which is (rather sparsely) documented <a href="http://devlicio.us/blogs/krzysztof_kozmic/archive/2009/12/10/castle-windsor-new-feature-dynamic-parameters-from-registration-site.aspx" rel="noreferrer">here</a> and in the last section <a href="http://docs.castleproject.org/Windsor.Inline-dependencies.ashx" rel="noreferrer">here</a>.</p> <p>What DynamicParameters lets you do is directly modify the collection of inline parameters that was supplied when the container was asked to resolve a component. This dictionary can then get passed on up the resolution pipeline, making it available to sub-dependencies.</p> <p><code>DynamicParameters</code> has three overloads, each taking a single delegate as its parameter. These delegate types aren't explicitly documented, so for the sake of posterity here's what ReSharper (I'm too lazy to download the source.) tells me their declarations look like:</p> <pre><code>public delegate void DynamicParametersDelegate(IKernel kernel, IDictionary parameters); public delegate ComponentReleasingDelegate DynamicParametersResolveDelegate(IKernel kernel, IDictionary parameters); public delegate ComponentReleasingDelegate DynamicParametersWithContextResolveDelegate(IKernel kernel, CreationContext creationContext, IDictionary parameters); </code></pre> <p><code>DynamicParametersDelegate</code> is for the most basic case, where you just need to supply parameters that won't be managed by the container. That probably works for me, but in keeping with my tendency to find the complicated option first I ended up making a beeline for the second option, which is to supply a delegate which manually pulls dynamic parameters out of the container. Since Windsor's managing the component's lifetime in that case, you need to make sure it's being released. That's where <code>DynamicParametersResolveDelegate</code> comes in - it's just like the first one, except it also returns a <code>ComponentReleasingDelegate</code> (<code>public delegate void ComponentReleasingDelegate(IKernel kernel);</code>) which Windsor can use to release components at the appropriate time.</p> <p>(The third, <code>DynamicParametersWithContextResolveDelegate</code>, is presumably for modifying the creation context. I don't know enough about how Windsor works to actually understand what the preceding sentence means, so I'll have to leave it at that.)</p> <p>That allows me replace the constructor from my example with the much better-looking:</p> <pre><code>public class Document { private readonly IDataSource _dataSource; private readonly IWidgetRepository _widgetRepository; private readonly IWhatsitRepository _whatsitRepository; public Document (IDataSource dataSource, IWidgetRepository widgetRepository, IWhatsitRepository whatsitRepository) { _dataSource = dataSource; _widgetRepository = widgetRepository; _whatsitRepository = whatsitRepository; } } </code></pre> <p>The factory is removed entirely. Instead, the magic goes in the component registration code for <code>IDocument</code>:</p> <pre><code>container.Register(Component.For&lt;IDocument&gt;() .ImplementedBy&lt;Document&gt;() .DynamicParameters( (k, d) =&gt; { // ask for an IDataSource, passing along any inline // parameters that were supplied in the request for // an IDocument var ds = k.Resolve&lt;IDataSource&gt;(d); // Add it to the dictionary. This makes it available // for use when resolving other dependencies in the tree. d.Add("DataSource", ds); // Finally, pass back a delegate which can be used to release it return (r) =&gt; r.ReleaseComponent(ds); })); </code></pre> <p>So now I can ask for an <code>IDocument</code> with exactly the line of code I had been looking for:</p> <pre><code>var document = container.Resolve&lt;IDocument&gt;(new { dataSourcePath = somePath }); </code></pre> <p>and the container starts by invoking that <code>DynamicParameters</code> delegate, which supplies the container with a <code>DataSource</code> for the supplied path. From there the container's able to figure the rest out for itself so that the same instance of <code>DataSource</code> gets passed to the constructors for all three of the other objects in the dependency graph.</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