Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I would have liked to have a bit more information before posting this answer, but Kelly put me on the spot. :) Telling me to put my code where my mouth is, so to speak.</p> <p>Like I said in my comment to Kelly, I disagree with moving the resolver/locator from a static implementation to an injected implementation. I agree with ChrisChris that the dependencies the derived type needs should be resolved in that class and not delegated to the base class. </p> <p>That said, here is how I would remove the service location...</p> <h3>Create Command Interface</h3> <p>First of all I would create a command interface for the specific implementation. In this case the types sent with the DoActions method are generated from attributes, so I would create an <code>IAttributeCommand</code>. I am adding a <code>Matches</code> method to the command in order to declare the command for use by certain types.</p> <pre><code>public interface IAttributeCommand { bool Matches(Type type); void Execute(); } </code></pre> <h3>Add Command Implementations</h3> <p>To implement the interface, I pass in the specific dependencies I need to execute my command (to be resolved by my container). I add a predicate to my Matches method, and define my Execute behavior.</p> <pre><code>public class MyTypeAttributeCommand : IAttributeCommand { MyDependency dependency; SomeOtherDependency otherDependency; public MyTypeAttributeCommand (MyDependency dependency, ISomeOtherDependency otherDependency) { this.dependency = dependency; this.otherDependency = otherDependency } public bool Matches(Type type) { return type==typeof(MyType) } public void Execute() { // do action using dependency/dependencies } } </code></pre> <h3>Register Commands with Container</h3> <p>In StructureMap (use your favorite container), I would register the array like so:</p> <pre><code>Scan(s=&gt; { s.AssembliesFromApplicationBaseDirectory(); s.AddAllTypesOf&lt;IAttributeCommand&gt;(); s.WithDefaultConventions(); } </code></pre> <h3>Select and Execute Commands Based on Type</h3> <p>Finally, on the base class, I define an <code>IAttributeCommand</code> array in my constructor arguments to be injected by the IOC container. When the derived type passes in the <code>types</code> array, I will execute the correct command based on the predicate.</p> <pre><code>public abstract class MyController : Controller { protected IAttributeCommand[] commands; public MyController(IAttributeCommand[] commands) { this.commands = commands); } protected void DoActions(Type[] types) { foreach(var type in types) { var command = commands.FirstOrDefault(x=&gt;x.Matches(type)); if (command==null) continue; command.Execute(); } } } </code></pre> <p>If you multiple commands can handle one type, you can change the implementation: <code>commands.Where(x=&gt;x.Matches(type)).ToList().ForEach(Execute);</code></p> <p>The effect is the same, but there is a subtle difference in how the class is constructed. The class has no coupling to an IOC container and there is no service location. The implementation is more testable as the class can be constructed with its real dependencies, with no need to wire up a container/resolver.</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