Home / StructureMap – Retrieving Services

StructureMap – Retrieving Services

Assuming that StructureMap has been configured with all the services and components that your system needs, it’s time to actually get to these services.  In normal usage, most service retrieval is done through the static ObjectFactory class, but the same functionality is available through the Container class by itself.

Next Prev

Get a Service by PluginType

By far and away the most common usage of a StructureMap is to simply retrieve and/or build the default instance of a given service type.  Let’s say that I have a service called “ITestEventPublisher.”  To get an object instance of this interface I can call the GetInstance() method of the ObjectFactory class.

With the generic method:

ITestEventPublisher publisher = ObjectFactory.GetInstance<ITestEventPublisher>();

Or without using generics (adds a cast):

ITestEventPublisher publisher = (ITestEventPublisher)ObjectFactory.GetInstance(typeof(ITestEventPublisher));

 

If there is only one Instance for a registered PluginType, that Instance will be assumed to be the default for the PluginType.  Otherwise, if there is more than one Instance for a PluginType, StructureMap must be explicitly told which Instance is the default, otherwise the call to GetInstance() will throw an exception (202).  If a call to GetInstance() is made for a concrete type that is not configured, StructureMap will attempt to create a new instance of this class.  See Concrete Types for more information.

Get a Service by PluginType and Name

Sometimes it’s advantageous to retrieve a “named” instance of a type.  Let’s say that you’re building a system that needs to connect to interface with multiple external shipping systems.  You’ve designed an interface for your system called IShippingSystem that hides the details of each external shipping behind adapters.  The rest of your code should only “know” how to interact with the IShippingSystem, but at some point, some class needs to know how to select and retrieve the proper instance of IShippingSystem.  Before the advent of IoC containers like StructureMap, you would have coded a Factory class and possibly a Builder class by hand to do the construction.  With StructureMap, this code is simply a call to the ObjectFactory.GetNamedInstance(Type, string) method.

 // With generics IShippingService internationalService = ObjectFactory.GetNamedInstance<IShippingService>("International"); IShippingService domesticService = ObjectFactory.GetNamedInstance<IShippingService>("Domestic"); // Without generics IShippingService internationalService2 = (IShippingService) ObjectFactory.GetNamedInstance(typeof(IShippingService), "International"); The GetNamedInstance(Type, string) method can be used like a generic factory: string serviceName = determineShippingService(); IShippingService service = ObjectFactory.GetNamedInstance<IShippingService>(serviceName);

Get all Instances of a PluginType

The ObjectFactory.GetAllInstances() method will construct and return a list of all the registered instances for a given PluginType.  This is a very useful feature for pluggable rules and chain of responsibility patterns where you might be adding new “handlers” to a system.  Here’s a typical scenario for this feature in an invoice processing system.  It’s a given that additional validation rules will need to be created and “plugged” into the system at a later date.  To handle this scenario, you might create an interface like this:

public interface InvoiceValidator
{
void Validate(Invoice invoice, ValidationResult result);
}

Now, let’s assume that you’ve configured zero or more Instances of the InvoiceValidator.  Somewhere in your invoice processing code you would use the ObjectFactory.GetAllInstances() method to retrieve all of the InvoiceValidator validation rules, and give each a chance to validate an Invoice object.

// With Generics
public ValidationResult RunRulesWithGenerics(Invoice invoice)
{
ValidationResult result = new ValidationResult();
IList<InvoiceValidator> validators = ObjectFactory.GetAllInstances<InvoiceValidator>();
foreach (var validator in validators)
{
validator.Validate(invoice, result);
}
return result;
}
// Without Generics
public ValidationResult RunRulesWithoutGenerics(Invoice invoice)
{
ValidationResult result = new ValidationResult();
IList validators = ObjectFactory.GetAllInstances(typeof(InvoiceValidator));
foreach (InvoiceValidator validator in validators)
{
validator.Validate(invoice, result);
}
return result;
}

By design, if no instances of InvoiceValidator are configured, a call to ObjectFactory.GetAllInstances<InvoiceValidator>() will return a zero length IList<InvoiceValidator>.  Previous versions of StructureMap would throw an exception in this case.

“Fill” all of the Dependencies of a Concrete Class

Sometimes you know what concrete class you need in a scenario, but you still want StructureMap to construct the class and “fill” it with any of its dependencies.  I’ve used this functionality in the past on large desktop clients for configuring navigation and menu commands.  In my desktop clients I use the Application Controller pattern to govern screen activation throughout the entire system.  Various screen elements can open new screens by telling the ApplicationController service to activate a new Presenter.  Let’s say that I have a screen for configuring shipments in the system.  That screen is managed by the ShippingScreenPresenter shown below:

public class ShippingScreenPresenter : IPresenter
{
private readonly IShippingService _service;
private readonly IRepository _repository;
public ShippingScreenPresenter(IShippingService service, IRepository repository)
{
_service = service;
_repository = repository;
}
}

Now, another Presenter or a menu item somewhere may want to activate a new ShippingScreenPresenter, and even “know” the concrete type ShippingScreenPresenter is the correct Presenter type, but won’t know how to create and attach instances of IShippingService or IRepository.  Finding, building, and attaching dependencies is the sole purpose of an IoC container, so let’s let StructureMap do that work for us in the ApplicationController.

public class ApplicationController
{
public void ActivateScreenFor<T>() where T : IPresenter
{
IPresenter presenter = ObjectFactory.FillDependencies<T>();
presenter.Activate();
}
}

The ShippingScreenPresenter can now be activated by this code:

ApplicationController controller = ObjectFactory.GetInstance<ApplicationController>();
controller.ActivateScreenFor<ShippingScreenPresenter>();

It is not necessary to configure the ShippingScreenPresenter class prior to calling ObjectFactory.FillDependencies<ShippingScreenPresenter>().  I should note that as of StructureMap 2.5, a call to ObjectFactory.GetInstance<ShippingScreenPresenter>() will behave identically to ObjectFactory.FillInstances<ShippingScreenPresenter>().  Originally, I felt that it was important to make this behavior explicit.  In later versions of StructureMap I’ve adopted more of an attitude of “just let it work.”

Passing Arguments to StructureMap at Runtime

Sometimes you want to explicitly pass an argument to StructureMap while letting it handle all of the other arguments and dependencies.  I run into this most frequently inside of desktop clients when I need to create and execute a screen or a Command using an existing screen element or object.  Let’s say we’re building a desktop client (WPF or WinForms) for invoice processing.  There is a screen somewhere that enables users to query for invoices and select one for editing.  The query screen needs to activate a new instance of the EditInvoicePresenter to start the invoice editing.  The EditInvoicePresenter declares its dependencies in its constructor function:

public EditInvoicePresenter(IRepository repository, IEditInvoiceView view, Invoice invoice)
{
_repository = repository;
_view = view;
_invoice = invoice;
}

The query screen knows the exact Invoice object to edit, but it certainly shouldn’t be responsible or knowledgeable about the other dependencies of the EditInvoicePresenter class.  That would represent the kind of tight coupling that makes systems resistent to change and harder to build.  What we want to do instead is tell StructureMap to build an EditInvoicePresenter with this particular Invoice.

EditInvoicePresenter presenter = ObjectFactory.With<Invoice>(invoice).GetInstance<EditInvoicePresenter>();
controller.ActivateScreen(presenter);

In the code above I used the With<T>(T dependency) method to pass in the explicit argument.  When StructureMap builds the EditInvoicePresenter object, it “knows” to use the invoice instance passed in for any argument of type Invoice in the EditInvoicePresenter.  I could also pass arguments by name and multiple arguments.  Here is another example from the unit tests.

Command command = ObjectFactory
.With("node").EqualTo(theNode)
.With(theTrade)
.GetInstance<Command>();

This functionality will also work for getting all instances of a given PluginType:

var theTrade = new Trade();
IList<TradeView> views = container.With(theTrade).GetAllInstances<TradeView>();

In some cases it might be more convenient to bypass the Fluent Interface for explicit arguments and pass the arguments in as a single dictionary like object called “ExplicitArguments:”

var theNode = new Node();
var theTrade = new Trade();
var args = new ExplicitArguments();
args.Set(theNode);
args.SetArg("trade", theTrade);
var command = container.GetInstance<Command>(args);

See “AutoWiring” for more information on this functionality.

“Try” to Get an Instance

A relatively new function of StructureMap is the ability to “try” to retrieve a service if it exists, but gracefully return null if the requested service does not exist.  Similar to the “TryParse()” methods in the .Net BCL, StructureMap exposes the “TryGetInstance” methods on both Container and the static ObjectFactory class:

object TryGetInstance(Type pluginType, string instanceKey); object TryGetInstance(Type pluginType); T TryGetInstance<T>(); T TryGetInstance<T>(string instanceKey);

As you might expect, these methods do not throw exceptions if the requested Instance does not exist:

[Test] public void TryGetInstanceViaNameAndGeneric_ReturnsNull_WhenTypeNotFound() { addColorInstance("Red"); addColorInstance("Orange"); addColorInstance("Blue"); // "Yellow" does not exist, so return null var instance = _container.TryGetInstance<Rule>("Yellow"); instance.ShouldBeNull(); } [Test] public void TryGetInstanceViaNameAndGeneric_ReturnsInstance_WhenTypeFound() { addColorInstance("Red"); addColorInstance("Orange"); addColorInstance("Blue"); // "Orange" exists, so an object should be returned var instance = _container.TryGetInstance<Rule>("Orange"); instance.ShouldBeOfType(typeof(ColorRule)); }
Next Prev