code magazine - article_ understanding dependency injection and those pesky containers
TRANSCRIPT
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
1/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true
Understanding DependencyInjection and Those PeskyContainers
We seem to be an industry enamored with buzz words. Even though
XmlHttpRequest has been around since the mid-90s, mainstream
programmers didnt give it a second thought until someone attachedthe term AJAX to it. The same is true for the never-ending quest to
put as many different words as we can in front of driven-
development. Another term that hit the scene in recent years is
dependency injection.
Bringing form to concepts that had been around for a while,
dependency injection (also known as DI) didnt kick into full force until
Martin Fowler wrote his famous article in 2004. The development
community grabbed onto the term and ran with it as the next big thing
we should all be doing, which is great - except for the fact that DI
seemed to be only a small part of what the community as a whole was
doing.
DI is an approach to providing one type with instances of the other
types on which it may depend, all while keeping the types decoupled
from one-another through the use of interfaces. Though defined in
Wikipedia as an approach to testing computer programs, its more
than just testing. That said, testing is an extremely good and
important reason to use DI, because with it, you can write decoupled
components, making it an integral part of good software testing. DI is
a form of Inversion of Control and is sometimes confused as being the
exact same thing. (Inversion of Control is a software principle whereas
control of a class initialization and execution is delegated to another
class, sometimes a container or a manager.) DI is a slight subset of
Inversion of Control.
Before digging into how implementat ions of DI work and how to use the
DI Container, you need to understand the concept of coupled and
decoupled software; the former being bad and the latter being thegood that becomes even better with DI.
Software Coupling
When writing software, its always a good idea to separate the
concerns of the types involved. Separation of concerns means that
each type has either only one, or a very small set of responsibilities. Of
course, if you translate your software to the line of business for which
its built, it immediately becomes apparent that a business process
involves many steps and therefore many types.
Most of the time, there is a need for one type to work hand-in-hand
with another and perhaps become a sub-part of another. In other
words, one class may depend on another. In fact, the parent type
would very likely contain an instance of the type on which it depends,
usually passed in through a constructor or a property.
The problem is that this creates a coupling between the two types.
Not only can the parent type not exist (or compile) without the
dependent type, it can be stuck with the way that type wants to do
things. Ill show you what I mean with an example of what not-to-do,
and then Ill show you techniques for solving the problem.
A Coupled Example
Lets take a look at a great example of a business process that can be
subdivided easily: e-commerce. Among other things, three of the
functions that are involved in a check-out process are:
By: Miguel Castro
Miguel is an a rchitect withIDesign who specializes inarchitecture consulting andbuilding .NET solutions. Heis a Microsoft MVP andINETA speak er and hasbeen a software developerfor over 22 years. With aMicrosoft background thatgoes all the way back to VB1.0 (and QuickBasic in fact),Miguel jumped on .NET as
soon as the first public Betawas released a nd hasprovided .NET solutions forclients around the country ina variety of industries. Heconsiders himself to be a.NET Develope r and Architectand has e qual love for bothVB and C#, and notolerance for langua gebigotry. Hes spoken atnumerous user groupsaround the country as wellas d eveloper conferences.
Hes the a uthor of theCode Breeze code-generator,which among things can befound on his Web site:
www.steelbluesolutions.com
Miguel currently lives inLincoln Park, NJ with his wifeElena and his daughterVictoria.
DI in .NET 1.1
Normally, I would
consider this a dead
product, but recently I
had a team of South
American developers
approach me after a
conference talk and
mention that they have
an old system written in
http://www.steelbluesolutions.com/mailto:[email protected]://www.steelbluesolutions.com/ -
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
2/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 2
payment processing
customer update
notification
After an order is placed, a customers credit card must be charged,
after which the customer record needs to be updated with the order
he/she placed and the product purchased. Lastly, the customer should
receive an email with their receipt and order summary. An additional
less critical process can be logging, which can take place at every
step for internal auditing purposes.
Lets start with a type that will contain the data involved in the
example. This type is called OrderInfo and can be seen in Listing 1.
To act upon this data, theres a type called Commerce with a method
called ProcessOrder, which performs all the aforementioned sub-
processes. In the interest of separating the concerns, I have created
types called BillingProcessor, Customer, Notifier, and Logger.
Each of them might be used from another class in another situation,
but its also a good idea not to put everything in the Commerce type
to encapsulate the check-out process.
The four classes are shown in Listing 2. Notice that in the interest of
simplicity, their methods have no real functionality. If they did, it
would be complex and involve several resources, such as a database,the file system, or additional APIs like an email sender and a payment
gateway. Also note that each of the methods of the four classes act
upon different pieces of the OrderInfo type.
The Commerce type incorporates each of the other four types by
receiving an instance of each in its constructor. Think of this as an
orchestrator or manager class. You can see this one in Listing 3.
Assuming that you have an instance of OrderInfo with the necessary
data, using the Commerce type can involve something like this:
Commerce commerce = new Commerce(
New BillingProcessor(),
New Customer(),
New Notifier(),New Logger());
commerce.ProcessOrder(orderInfo);
The problem here is two-fold. First, the Commerce type is completely
and utterly coupled to the other four types. Not only can it not work
without them, but its totally locked into the implementation that each
provides. What if the system youre writing changes payment
gateways in the future? If your application were written like the
example snippet above, you would have to rewrite the entire
BillingProcessor class, only to rewrite it again later. Of course you
can have two different classes, but that would mean changing the
code in the Commerce class in order to alter which new or different
types gets received.
The second problem is that the Commerce class is difficult to test.
Eventually you will have to test the production functionality of the four
classes that house the sub-processes, but what if you want to test
the ProcessOrder method of the Commerce class to see how it
handles the combination of the four processes without having to deal
with the resource access that those processes might undertake?
Writing a unit test for this method means having to bring in all four of
the other types and whatever resource access was written into them.
" coupled = bad "
.NET 1.1. They still
support and enhance
this system but for
corporate policy
reasons, cannot
upgrade it beyond 1.1.
Interested in
introducing DI to it,
they asked me what
their options are. To
the best of my
knowledge, none of the
DI containers
mentioned in this article
support .NET 1.1
because of their use of
generics and/or lambda
expressions. The make-
shift container I wrote
for this article can be
easily modified to favor
typeof statements
over generics. Though
nowhere as feature-rich
as any of the other
containers, it can
certainly fulfill the basicDI requirements of .NET
1.1 applications.
DI and UnitTesting
Although some may
argue that DI is allabout unit testing, its
actually about writing
decoupled software.
Thats what I wanted
the focus of this article
to be and thats why I
didnt get deeply into
the practice of unit
testing or TDD. Using a
DI container to assist
you in developing
components which
depend on interfaces
rather than classesallows you to test
those components with
test implementations of
their dependencies,
dependencies which
can otherwise involve
more complex
resources, such as
databases. Its not the
DI container that
assists you in testing;
its the practices on
which you are forced to
focus when involvin a
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
3/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 3
This is coupled behavior, and remember coupled = bad. But I didnt
spend all that time writing code only to tell you not to do it this way.
Im going to make adjustments to the code above in order to decouple
my components and make everything more extensible and testable.
A Decoupled Example
The first step to decoupling application components is to abstract out
the implementation from the definition in the four process classes. This
way, you can provide a production implementation and test
implementation later. So the first thing to do is refactor the definition
of the classes out to four interfaces, as shown in Listing 4.
Then youll modify the four process classes to implement their
appropriate interface. In the interest of space, only the
BillingProcessor class is included in this code snippet, but this same
pattern applies to the other three process classes.
Public class BillingProcessor : IBillingProcessor
{
Void IBillingProcessor.ProcessPayment(
String customer,
String creditCard,
Double price)
{
// Perform billing gateway processing
}}
You can now write as many different billing processors as you want,
each providing a different implementation of the interface. The key
now is to modify the Commerce class so that it receives injections by
way of the interfaces and not the concrete types as before. The
rewrite of the Commerce class is in Listing 5.
Ive provided the entire class again so you can see that the only thing
that has changed is the type of the constructor arguments and the
class fields. The method calls in the ProcessOrder method are exactly
the same. The Commerce class makes these method calls and leaves
their actual function to whatever the implementation classes are that
were sent through the constructor. The Commerce typesdependencies were injected through the constructor without being
coupled to the Commerce type. The only coupling that took place is
through the interfaces and they act as plumbing. This is good coupling.
This light coupling is DI in its simplest form. The components have
been decoupled from one another and through the use of interfaces,
the application can grow and change in the future and components
can be swapped if and when necessary. In fact, in the context of
Visual Studio projects, the Commerce class can sit in one assembly
(possibly the main application), the process classes in another (or
even four different ones), and both sides share a third assembly
containing the interfaces.
If you were to write a unit test against the Commerce class
ProcessOrder method, you would no longer need the productioninstances of the four process classes, and certainly not even a
reference to the assembly in which they live. You can write test
versions that implement the interfaces in a totally different way,
perhaps doing nothing at all except providing a dummy return value
when one is expected. In fac t, ideally, you wouldnt need to write test
implementations at all but can use a mocking framework to create
them on-the- fly within the unit t est itself.
The one thing that hasnt changed with this refactoring is the fact
that whatever called the Commerce class still needs to instantiate
the four process classes in order to inject them into the instance of
Commerce. This is where a DI container will come in handy.
DI container that does
so. Testing is an
important and involved
topic and I felt it would
digress from the core
intent of this article.
MEF 2
I believe MEF to be a
first-class c itizen in the
world of DI and that
belief is solidified
further with MEF 2. In
addition to providing
better debugging
capability for
composition errors (a
headache for many MEF
users), MEF 2 adds
some great registration
features with a new
RegistrationBuilder
class.
The c lass exposes an
awesome fluent API
that lets you export
types using various
techniques without the
need to use attribute
decorations. Among
these are direct
replacements for the
existing attributes aswell as some very cool
new techniques, such
as exporting types that
contain a certain
property. MEF 2
Preview is currently in
its fifth iteration and
can be obtained from
http://mef.codeplex.com/releases/view/79090.
You can also find a
good Code-Project
article at
http://www.codeproject.c om/Articles/366583/ME
2-Preview-Beginners-
Guide thats worthchecking out.
http://www.codeproject.com/Articles/366583/MEF-2-Preview-Beginners-Guidehttp://mef.codeplex.com/releases/view/79090 -
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
4/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 4
DI containers are about two things, R&R. No, not rest and relaxation.
Im talking about registration and resolving. These are the acts of
storing a list of types and later retrieving instances of them at will.
" DI containers are about two things,R&R (registration and resolving) "
The DI container is the tool that turns DI into architectural patterns
that lets you satisfy a types dependencies easily and automatically. Itis a repository that typically associates interfaces with concrete
types.
How a DI Container Works
DI containers all work in a very similar fashion. At the beginning of an
applications execution-cycle, you need to register associations of
concrete types to the interfaces that they implement. What makes
containers different from one another is the way registration
functionality is exposed. Later, when an instance of an interface
implementation is requested, the container can offer an instance of
the appropriate concrete type, called resolving. What makes the
process of the instance assignment special is that it happens
recursively, as many times as necessary.
At the start of an app, you might register 20 types with 20 interfaces.
When you request a type from the container by specifying an
interface, not only do you get an instance of the associated type, but
the container offers a bit more. The container looks at either the
constructor arguments or the public properties and determines whether
they are interface types. If they are interface types, it attempts to
resolve them as well and set the argument or property value to the
instance it resolved. The container then repeats the process for each
of the resolved types as well. By the time it returns the type you
requested, it has all its dependencies with it, and their dependencies,
and so on down the line.
Because essentially all containers work this way, theres no better way
to fully understand what goes on behind the scene than to see thecode of a very simple DI container. Any container you use later, no
matter how complex it may seem, is essentially a variation of what Ill
show you here.
Because you need to store associations of interfaces to concrete
types, start by writing a simple class to represent this association:
Public class ContainerItem
{
Public Type AbstractionType { get; set; }
Public Type ConcreteType { get; set; }
}
Now its a matter of writing a class that will serve as the container and
expose an API for adding instances ofContainerItem types. Listing 6
shows the container class.
As you can see, all the code does is expose a method called Register,
which will let you store an association of an interface to a concrete
type. Each association is stored in the container list, _Registrations.
Now you just need to expose another method that will let you ask for
whatever type is associated to a given interface. Call this method
CreateType.
Public T CreateType() where T : class
{
Object instance = null;
Type type = typeof(T);
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
5/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 5
ContainerItem containerItem =
_Registrations.Where(item =>
item.AbstractionType.
Equals(type)).FirstOrDefault();
If (containerItem != null)
Instance = Activator.CreateInstance(
ContainerItem.ConcreteType);
Return instance;
}
This simple version of the CreateType method looks for an item in the
registration list where the AbstractionType property is equal to the
type requested in the generic argument. If it finds an entry, it takes
the ConcreteType property and creates an instance of it to return.
Where a DI container provides its real value is in the recursive walk it
takes through the properties and/or constructor arguments of the
types it resolves, in order to further resolve its dependencies. Modify
the CreateType method to do just that.
You also need to return an instance of the requested type if it isnt an
interface. This way, the CreateType method can be used to
instantiate a type that might not have been registered earlier, but still
walk through it to resolve its dependencies. Listing 7 shows themodified CreateType method.
It doesnt take a lot of code to take the type that is found in the list,
go through the arguments of its constructor and if they are of an
interface type, attempt to resolve them. You continue to do the same
for those types, and so on until the type initially requested is returned.
In order to use this simple container, modify the e-commerce code
snippet I wrote back in the A Coupled Example section. The
Commerce class will remain exactly the same, as will the interfaces
and the four processes. Only the client must change.
Container container = new Container();
container.Register();
container.Register();
container.Register();
container.Register();
OrderInfo orderInfo = new OrderInfo()
{
CustomerName = "Miguel Castro",
Email = "[email protected]",
Product = "Laptop",
Price = 1200,
CreditCard = "1234567890"
};
Commerce commerce = container.CreateType();
commerce.ProcessOrder(orderInfo);
Keep in mind that in a real application, the registrations occur at
startup and container is saved in a way that it can be accessed from
anywhere. If any of the registered types were to also have interface-
based constructor arguments, they would be resolved also, provided
those interfaces were registered in the container.
Any container you decide to use works in a very similar fashion to
what Ive just demonstrated. There are many DI containers for you to
choose from, according to your needs.
mailto://[email protected] -
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
6/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 6
u c our oug ome opu ar on a ners
Although this article is not a tutorial on all the available DI containers
out there, its worth talking briefly about a few of them and showing
some simple examples.
Unity is Microsofts answer to the call for DI containers. It works in a
very similar fashion to the make-shift container you looked at in this
article. In fact, the only difference youll note is the name of the
container type and the names of the methods used to register and
resolve. Heres the same container-usage example I used earlier, but
using Microsoft Unity instead:
UnityContainer container = new UnityContainer();
container.RegisterType();
container.RegisterType();
container.RegisterType();
container.RegisterType();
OrderInfo orderInfo = new OrderInfo()
{
CustomerName = "Miguel Castro",
Email = "[email protected]",
Product = "Laptop",
Price = 1200,CreditCard = "1234567890"
};
Commerce commerce =
container.Resolve();
commerce.ProcessOrder(orderInfo);
Other popular containers include Castle Windsor, MEF, NInject,
StructureMap, and Spring.NET. Each offers slightly different features
than the others, but they all offer a way to register types and to
resolve them and their dependencies. To keep the article short, and
because they are very similar, details of these other containers wont
be covered here. The difference in usage scenarios is purely
syntactical and there are plenty of resources available online to showyou how to use them. I will demonstrate an example using one of
them: Castle Windsor.
Heres Castle Windsor. In the interest of space, Ill leave out the
creation of the OrderInfo class:
WindsorContainer container = new
WindsorContainer();
container.Register(Component.For());
container.Register(
Component.For().
ImplementedBy());
container.Register(Component.For().ImplementedBy());
container.Register(Component.For().
ImplementedBy());
container.Register(Component.For().
ImplementedBy());
Commerce commerce =
container.Resolve();
commerce.ProcessOrder(orderInfo);
As you can see, the process is the same, although the API exposed by
Castle Windsor is very different from that of Unity. Notice that when
using this container, any class to be resolved needs first to be
mailto://[email protected] -
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
7/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 7
reg s ere , nc u ng e ommerce c ass, an no ce a s c ass
is registered on its own and not associated to any interface.
Lets look at one more by seeing what the syntax for NInject looks like:
IKernel container = new StandardKernel();
container.Bind().
To();
container.Bind().To();
container.Bind().To();
container.Bind().To();
Commerce commerce = container.Get();
commerce.ProcessOrder(orderInfo);
Microsoft provides another container called the Managed Extensibility
Framework, or MEF. Although many will argue that MEF is not really a
DI container, including Microsoft themselves, it does provide the ability
to serve as one; and quite well. MEF provides the ability to develop
plug-ins and extensible applications in a simple and consistent manner.
It provides a container class that stores registrations as well as the
ability to resolve types and dependencies: the essence of a DI
container.
What makes MEF very unique is its ability to discover types insteadof forcing you to list registrations through code at application startup.
MEF lets you decorate types to be registered and associate them with
an interface in that decoration. It can then go out and discover said
types by building a catalog, which it can do in a number of ways. Lets
take a look at how types are registered in MEF:
[Export(typeof(IBillingProcessor))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class TestBillingProcessor
: IBillingProcessor
{
void IBillingProcessor.ProcessPayment(
string customer, string creditCard,
double price)
{
// perform billing gateway processing
}
}
A class dependencies, as in the case of the Commerce class, need
to be marked in MEF. This can be done with either the
ImportingConstructorattribute for constructor injection, or with the
Import attribute for property injection. The MEF version of the
Commerce class is in Listing 8.
Note that the Commerce class is also exported. Later, when resolved
through the container, MEF examines the attributes to satisfy its
dependencies.
In order to register the necessary types, you need to create a catalog
of types. MEF provides several ways of cataloging types but the
easiest is the assembly catalog. This allows you to point to an
assembly and let MEF scan it for types that offer type-export
decorations. You can also combine more than one catalog using an
aggregate catalog. In this example, you create an aggregate catalog
but only add one assembly catalog to it.
AggregateCatalog catalog =
new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(
Assembly.GetExecutingAssembly()));
Container = new CompositionContainer(catalog);
&
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
8/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 8
T e Compos t onConta ner c ass s MEF s DI conta ner. Here, I u t a
catalog from the current assembly, which is where the Commerce
class and the four process classes reside. Later, to resolve the
Commerce class and its dependencies, I used the
GetExportedValue method like this:
Commerce commerce =
Container.GetExportedValue();
commerce.ProcessOrder(orderInfo);
MEF is included with.NET Framework and its discovery mechanism gives
it a nice quality over other containers. The trade-off is that your type
registrations are scattered throughout all your classes in the way of
attribute decorations. I personally do not consider this a negative
thing. I like explicit code and MEFs attribute model allows me to
always know if a class is being used by MEF or not. This is something I
would not be able to tell if I were using a different DI container.
Practical Application of Dependency Injection andContainers
Now, for the fun part. All of this would just be cool code if you dont
know how to apply it in your applications. Im going to show you how
to implement a DI container into three types of applications: WPF,
WebForms, and MVC. With a little effort, you can modify the WPF
example to work in a Silverlight scenario as well as a Metro app
scenario.
The other two examples both apply to ASP.NET, but Ill provide you
with both since WebForms and MVC differ quite a bit in the way they
serve views. Ill use MEF as the container but the code I make
available for download at the end of article also provides examples in
Unity. Other containers would be a variation of one of the two
examples.
WPF
WPF continues to be Microsofts premier development platform for its
XAML stack. The other two are Silverlight and Metro apps. With
certain exceptions here and there, as well some framework differences,
development on all three platforms is very similar. Views consist of
XAML markup working in conjunction with code-behind pages. A
popular pattern that is often associated almost synonymously with
WPF (all XAML-stack platforms, in fact) is Model-View-ViewModel, or
MVVM. Ill demonstrate DI in a WPF application that implements the
MVVM pattern.
A WPF application has one main view when the application starts up,
which hosts two other views within it and offers a button to toggle
between the two. The two views are CustomerListView and
CustomerView, and the hosting view is MainWindowView. Each of
the three views has a corresponding view-model class under the same
name but with a view-model suffix instead ofView.
A service class provides data models for both the
CustomerListViewModel and CustomerViewModel classes. Theclass is called CustomerRepository and is responsible for obtaining
information from a database. The CustomerRepository class
implements an interface called ICustomerRepository and the view-
models contain properties of the interface type, not the concrete c lass
type.
As you may be able to tell, Ive set the scene for class dependencies
here. The MainWindowViewModel class has a dependency on the
other two view-model classes; and each of those two have a
dependency on an implementation of the ICustomerRepository
interface. In order to satisfy all dependencies, all class instances need
to be served up by a DI container. The first order of business is to set
one up and register types and associations with it.
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
9/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 9
MEF uses discovery to find types and their associations to interfaces,
so you have to decorate any class you want to export so that MEF will
find it. Ill leave out most of the class implementation code for brevity,
but Ill show the containment of the dependent types to explain
afterward. Be sure to check the sidebar to see where you can
download the entire solution of projects.
The MainWindowViewModel class contains the two other view-
models and toggles between them using a command and property. The
class is shown in Listing 9.
In this class, the dependencies are satisfied using property injection.As you can see, there are two properties of the types of the two
other view-models and they are both decorated with the Import
attribute. Later when this class is resolved, instances of those two
view-models will be injected into these two properties. As you can
probably guess, both of those view-models will also be exported so
that MEF discovers and registers them. Because this class will be
resolved and dependencies injected into them using properties, the
class will need to be instantiated before its dependencies are injected,
unlike a constructor injection technique.
To properly initialize and use this view-model, I need to set the
CurrentViewModel property to the value _CustomerListViewModel
property. The problem is that I cant do this in the constructor
because upon construction, the dependencies have not yet been
injected. For this kind of situation, MEF gives us theIPartImportsSatisfiedNotification interface. When MEF resolves this
view-model class, it checks for this interface and if it finds it
implemented, it executes the OnImportsSatisfied method after it
finishes injecting dependencies.
The other two view-model classes will also be exported and contain
the repository as a dependency. For variation of technique
demonstration, Ive chosen to use constructor injection in these two
cases.
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class CustomerListViewModel
: view-modelBase{
[ImportingConstructor]
public CustomerListViewModel(
ICustomerRepository customerRepository)
{
_CustomersModel =
customerRepository.GetAll();
}
}
And
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class CustomerViewModel : view-modelBase{
[ImportingConstructor]
public CustomerViewModel(
ICustomerRepository customerRepository)
{
_CustomerModel =
customerRepository.GetById(1);
}
}
Marking the constructors with the ImportingConstructor attribute
provides the constructor injection Im looking for. The last thing I need
is to export the CustomerRepository class and to associate it to the
ICustomerRe ositor interface.
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
10/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 10
[Export(typeof(ICustomerRepository))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class CustomerRespository
: ICustomerRepository
{
public Customer GetById(int id) . . .
public List GetAll() . . .
public void Update(Customer customer) . . .
}
Remember, due to the recursive nature of the DI container type-resolving process, resolving the MainWindowViewModel class later
will resolve its dependencies, the other view-models. Resolving those
two classes will resolve their dependencies, the CustomerRepository
class.
Now that the classes are set up properly, the WPF application needs
to discover them so the container can be assembled. The easiest
place to do this is in the code-behind of the App.xaml file by
overriding the OnStartup method. This will ensure that it happens at
application startup.
public partial class App : Application
{
public static CompositionContainer Container;
protected override void OnStartup( StartupEventArgs e)
{
AggregateCatalog catalog =
new AggregateCatalog();
catalog.Catalogs.Add(
new AssemblyCatalog(
Assembly.GetExecutingAssembly()));
Container = new CompositionContainer(
catalog);
base.OnStartup(e);
}
}
Im also storing the instance of the container in a static property so it
can be accessed from anywhere in the application if needed.
Because the MainWindowView window is set up to be the startup
window, its code-behind class executes when the application starts up
after the aforementioned OnStartup method executes. The view-
model for this window needs to be set to the windows DataContext
property. The other views will have theirs set through data templates
and a standard view-model-to-Content property technique, but since
this is the first one, it needs to be done manually. Rather than
manually instantiating an instance of the MainWindowViewModel
class, Im going to ask the container for it, and Ill do it in the
MainWindowView class constructor.
public MainWindow()
{
InitializeComponent();
MainWindowViewModel view-model =
App.Container.
GetExportedValue();
this.DataContext = view-model;
}
This sets the DI wheels into motion. Nowhere else in this project will
you be instantiating any view-model manually, or any type in which
view-models are dependent. The application is constructed the way
modern WPF, Silverlight, and Metro applications are written: using
containment view-switchin and with view-models mimickin the
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
11/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 1
containment relationship between views.
When you write unit tests to test the CustomerRepository, you use
that actual class so that you can test the database functionality, but
when you write unit tests to test the view-models, you dont
necessarily need to access the database. Because the view-models
depend on a type that implements the ICustomerRepository, you
can provide view-models with either a test implementation or even
better Id use a mocking framework.
MVC
ASP.NET MVC is a terrific framework that allows us to develop Web
applications in keeping with good architectural and development
principles, including separation of concerns, testability, and
dependency injection.
Like the view-models in the previous example, the controller classes
youll use in the MVC example will depend on the same
ICustomerRepository dependency. The CustomerRepository class
and the ICustomerRepository interface remain exactly the same as
in the WPF example, so I wont repeat it here. The HomeController
class will have its dependency injected using constructor injection and
can be seen in Listing 10.
Notice that this class is exported with an identifier string that is the
same name as the controller class but without the word Controller.Also note that the class is associated with the IController interface.
This is the case with all other controllers that I write, and because
there will be many classes associated with the same interface; the
string identifier becomes important when the controllers get resolved
later.
When they architected ASP.NET MVC, they took the idea of decoupled
types right into the design of the framework. When you write a
controller class in ASP.NET MVC, a controller factory instantiates the
class and executes the desired action. The controller factory can be
unhooked and a replacement put in its place so you can intercept how
controller classes are served up. Because this controller has
dependencies that need to be injected into it, it needs to be
instantiated through the container.
You have a few choices in the way you implement DI in the MVC
application, and the first choice is where to setup the container. You
can do it in the Global.asax.csclass but what you really need to do
in this file is to hook up a replacement to the default controller
factory. You need to write a class called MefControllerFactory and
hook it up in the application startup event of the Global.asax.cs
class.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current
.SetControllerFactory(
new MefControllerFactory());
}
The line I added to this already-existing event is the last one, where I
set the controller factory to an instance of the replacement class. You
will set up the container in the MefControllerFactory class. The
controller factory is in Listing 11.
I could have written a controller factory entirely from scratch by
implementing the IController interface, but it was much easier to
http://global.asax.cs/http://global.asax.cs/ -
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
12/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 12
method. The constructor contains the code that will discover all of my
exported types and store the container in a class variable for later
use. When a controller is requested during a browser query, this class
will get hit for a controller and it will execute the CreateController
method. The argument, controllerName , will receive the name of the
controller as it appeared in the URL used to query the application,
without the word Controller in it. Thiss why I exported the
HomeController and identified it with the name, Home. The
CreateController method queries the controller for a specific type
that implements the IController interface using the identifier name.
When the controller class gets resolved, the ICustomerRepository
dependency will be injected into the constructor argument since Idecorated the constructor with ImportingConstructor. From here,
its available to any ac tion that needs to use it.
As in the WPF example, you can write unit tests to test the controller
act ions and use a mock object to sat isfy the necessary dependencies.
I want to take advantage of an opportunity presented in this example
to demonstrate a way you can extend MEF to provide something that
was not built into it. The controller factory has another available
method that can be overridden in order to obtain a controller class,
called GetControllerInstance. This method does not receive a
controller name in an argument but instead receives a Type. This type
corresponds to the controller class that needs to be instantiated. This
seems a whole lot easier because then I can simply export the
controller, with no need to identify it with a name or associate it with
the IController interface. The problem is that the MEF container does
not come equipped with a method to resolve a type given an actual
Type argument; so Ill extend it by writing an extension method. You
can see my extension method in Listing 12.
This extension method adds a method to the container called
GetExportedValueByType. This method scans the registered types
and looks for one that matches the Type argument sent into the
method. Now you can resolve the controllers in the controller factory
without the need for its name, and you can change the exportation of
the controller classes to a simple [Export].
protected override IController
GetControllerInstance(RequestContext requestContext,
Type controllerType)
{
return _Container.GetExportedValueByType(
controllerType) as IController;
}
Theres a new feature added to MVC 3 called the dependency
resolver which offers yet another way of resolving controllers. In fact,
it allows you to resolve many other things in ASP.NET MVC. The full
code for this article includes examples of its usage, but for the
purposes of brevity I will not discuss it here.
WebForms
The last example of DI usage I will demonstrate involves a technology
that many think cannot be subject to DI and called it one of its
shortcomings: ASP.NET WebForms. Unlike ASP.NET MVC, the
WebForms architecture is one of a much coupled nature. ASPX pages
are combined with code-behind class and at runtime, the two are
combined and a virtual page class is created and executed. This is
where the page life-cycle comes from and why we get several great
events into which we can inject code. Sadly this also means that the
code-behind class cannot be instantiated and served by an outside
influence, since it needs to remain under ASP.NET management the
entire time. However, like ASP.NET MVC, there is a mechanism in place
that builds the classes for us and its into that you can tap.
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
13/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 13
.
PageHandlerFactory. This class is installed as an HTTP Handler
Factory and is meant to return an instance of an HTTP Handler; in this
case, a class that is derivative from System.Web.UI.Page. What you
can do is override this process with your own handler factory that
derives from the standard one. By overriding the method that returns
the Page class, you can call upon the base so that you can obtain
that page class without interfering with the process, then you can
inject your dependencies.
Construction injection is out of the question here because you cannot
resolve the page class using a container, so you have to resort to
property injection. MEF lets you do this easily by declaring thedependency properties and decorating them with the Import attribute.
The dependency class in this case remains the CustomerRepository
class and its configuration for MEF compliance remains the same as in
the two previous examples. The ASPX pages code-behind class does
not need to get decorated with anything at the c lass level, since it will
not be served through the container, only at the property level for the
dependencies it may contain.
public partial class Customers
: System.Web.UI.Page
{
[Import]
ICustomerRepository _CustomerRepository;
protected void Page_Load(object sender,
EventArgs e)
{
grdCustomers.DataSource =
_CustomerRepository.GetAll();
grdCustomers.DataBind();
}
}
Ill show you how to resolve that class dependencies in a few minutes,
but first you have to configure the container in the WebForms
application.
As in the MVC example, you can go to the Application_Start event in
the Global.asax.csfile to set up the container.
protected void Application_Start(object sender,
EventArgs e)
{
AggregateCatalog catalog =
new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(
Assembly.GetExecutingAssembly()));
CompositionContainer container =
new CompositionContainer(catalog);
Application["Container"] = container;
}
Also like the previous example, youll use the Application store to
hang on to the container. Next, you have to create a class that will
replace the PageHandlerFactory class.
Ive created a class called MefPageHandlerFactory, which will
override the GetHandler method and obtain the Page class by calling
on the base. Listing 13 shows the factory class.
After youve obtained the page class, youll use a feature that MEF
provides. By calling the SatisfyImportsOnce method, youre telling
MEF to run the class sent into the argument through the resolve
process and attempt to resolve any properties it finds decorated with
the Import attribute. When the page is returned at the end of the
method, it will be equipped with instances of the proper classes in its
http://global.asax.cs/http://system.web.ui/http://system.web.ui/ -
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
14/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 14
.
The last thing you need to do is install the new handler factory in the
web.config file by assigning it as the handler to be used for anything
with an aspx extension.
Unfortunately, unit-testing WebForms code-behind pages is next to
impossible as the class cannot be instantiated on its own. This remains
one of WebForms shortcomings.
That being said, I think I need to say something in defense of
WebForms. Business code should be written so it can be tested on its
own, regardless of the client implementing it. ASP.NET MVC lets us unit
test controller actions but that does not mean this should serve as the
test for your business logic. Controller actions return a certain result
based on input arguments and thats what should be tested by unit
tests, not that the customer was saved properly. If you design
applications to accommodate this, the lack of ability to test a
WebForms code-behind class will seem a little less significant and
WebForms may be able to retain some of the glory it once carried as afirst class development platform for Web applications. I say this as an
active developer in both the WebForms and MVC platforms.
Conclusion
As you can see, dependency injection solves several problem areas in
development. It allows you to decouple code components from one
another and it lets you stop worrying about instantiating classes to
send into other classes. Solving these two problems also sets up your
software for easy testability later.
There are many elements of DI that I couldnt cover in this article.
Among them is the resolving multiple implementations of an interface
as well as defining which of several registered implementations gets
resolved, which is possible through configuration files. Most containers
provide both of these features, each in its own variety.
I also couldnt cover every container out there. Besides MEF, Unity,
NInject, and Castle Windsor, there are also StructureMap and
Spring.NET. I apologize to the developers of the latter two for not
providing coverage within this article but Ill go on record as saying
that both StructureMap and Spring.NET are first-class citizens in the
DI world and top-shelf products.
The examples in this article should give you a great head-start on
using dependency injection. The three scenarios should also serve as a
good kick-off point for just about any application you are writing.
Whether youre using a Microsoft container or a third-party one, using
DI in your applications will ensure that youre writing decoupled,manageable, and testable components, and also very importantly, its
really, really cool.
Miguel Castro
Listing 1: OrderInfo class
public class OrderInfo
{
public string CustomerName { get; set; }
public string Email { get; set; }
public string Product { get; set; }
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
15/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 15
public string CreditCard { get; set; }
}
Listing 2: The four process classes
public class BillingProcessor
{
public void ProcessPayment(string customer,
string creditCard, double price)
{ // perform billing gateway processing
}
}
public class Customer
{
public void UpdateCustomerOrder(string customer,
string product)
{
// update customer record with purchase
}
}
public class Notifier
{ public void SendReceipt(OrderInfo orderInfo)
{
// send email to customer with receipt
}
}
public class Logger
{
public void Log(string message)
{
// log message to log file
}
}
Listing 3: Commerce class
public class Commerce
{
public Commerce(BillingProcessor billingProcessor,
Customer customer,
Notifier notifier,
Logger logger)
{
_BillingProcessor = billingProcessor;
_Customer = customer;
_Notifier = notifier;
_Logger = logger;
}
BillingProcessor _BillingProcessor;
Customer _Customer;
Notifier _Notifier;
Logger _Logger;
public void ProcessOrder(OrderInfo orderInfo)
{
_BillingProcessor.ProcessPayment(
orderInfo.CustomerName,
orderInfo.CreditCard,
orderInfo.Price);
_Logger.Log("Billing processed");
_Customer.UpdateCustomerOrder(
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
16/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 16
orderInfo.CustomerName,
orderInfo.Product);
_Logger.Log("Customer updated");
_Notifier.SendReceipt(orderInfo);
_Logger.Log("Receipt sent");
}
}
Listing 4: Process classes refactored to interfaces
public interface IBillingProcessor{
void ProcessPayment(string customer, string creditCard,
double price);
}
public interface ICustomer
{
void UpdateCustomerOrder(string customer,
string product);
}
public interface INotifier
{
void SendReceipt(OrderInfo orderInfo);
}
public interface ILogger
{ void Log(string message);
}
Listing 5: Commerce class rewrite using interfaces
public class Commerce
{
public Commerce(IBillingProcessor billingProcessor,
ICustomer customer,
INotifier notifier,
ILogger logger)
{
_BillingProcessor = billingProcessor;_Customer = customer;
_Notifier = notifier;
_Logger = logger;
}
IBillingProcessor _BillingProcessor;
ICustomer _Customer;
INotifier _Notifier;
ILogger _Logger;
public void ProcessOrder(OrderInfo orderInfo)
{
_BillingProcessor.ProcessPayment(
orderInfo.CustomerName, orderInfo.CreditCard,
orderInfo.Price);_Logger.Log("Billing processed");
_Customer.UpdateCustomerOrder(orderInfo.CustomerName,
orderInfo.Product);
_Logger.Log("Customer updated");
_Notifier.SendReceipt(orderInfo);
_Logger.Log("Receipt sent");
}
}
Listing 6: My makeshift DI container
public class Container
{
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
17/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 17
public Container()
{
_Registrations = new List();
}
public void Register()
where U : class, new()
{
Type abstractionType = typeof(T);
Type concreteType = typeof(U);
if (!abstractionType.IsInterface) throw new ApplicationException(
"First generic argument must be an
interface type.");
_Registrations.Add(new ContainerItem()
{
AbstractionType = abstractionType,
ConcreteType = concreteType
});
}
List _Registrations;
}
Listing 7: Modified CreateType function and support code
public T CreateType() where T : class
{
Type type = typeof(T);
return (T)GetConcreteType(type);
}
object GetConcreteType(Type typeToResolve)
{
ContainerItem containerItem =
_Registrations.Where(item =>
item.AbstractionType.Equals(
typeToResolve)).FirstOrDefault();
if (containerItem != null)
return GetTypeInstance(containerItem.ConcreteType);
else
return GetTypeInstance(typeToResolve);
}
object GetTypeInstance(Type type)
{
object instance = null;
ConstructorInfo[] constructors = type.GetConstructors();
if (constructors.Length > 0)
{
ConstructorInfo constructor = constructors[0];
List constructorArguments =
new List();
ParameterInfo[] parameters =
constructor.GetParameters();
foreach (ParameterInfo parameter in parameters)
{
object parameterInstance = null;
if (parameter.ParameterType.IsInterface)
parameterInstance = GetConcreteType(
parameter.ParameterType);
constructorArguments.Add(parameterInstance);
}
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
18/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 18
instance = Activator.CreateInstance(
type, constructorArguments.ToArray());
}
return instance;
}
Listing 8: MEF-aware Commerce class
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Commerce
{
[ImportingConstructor]
public Commerce(IBillingProcessor billingProcessor,
ICustomer customer, INotifier notifier,
ILogger logger)
{
_BillingProcessor = billingProcessor;
_Customer = customer;
_Notifier = notifier;
_Logger = logger;
}
IBillingProcessor _BillingProcessor;
OR
public Commerce()
{
}
[Import]
IBillingProcessor _BillingProcessor;
}
Listing 9: MainWindowViewModel class
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MainWindowViewModel
ViewModelBase, IPartImportsSatisfiedNotification
{
[Import]
CustomerListViewModel _CustomerListViewModel;
[Import]
CustomerViewModel _CustomerViewModel;
ViewModelBase _CurrentViewModel;
public ICommand ToggleViewCommand { get; private set; }
public ViewModelBase CurrentViewModel
{
get { return _CurrentViewModel; }
set
{
_CurrentViewModel = value;
OnPropertyChanged("CurrentViewModel");
}
}
public void OnImportsSatisfied()
{
=
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
19/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
www.code-magazine.com/articleprint.aspx?quickid=1210031&printmode=true 19
_
}
}
Listing 10: MVC controller
[Export("Home", typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller
{
[ImportingConstructor]
public HomeController(
ICustomerRepository customerRepository)
{
_CustomerRepository = customerRepository;
}
ICustomerRepository _CustomerRepository;
public ActionResult Customers()
{
IEnumerable customers =
_CustomerRepository.GetAll();
return View(customers);
}
}
Listing 11: MVC controller factory
public class MefControllerFactory : DefaultControllerFactory
{
private CompositionContainer _Container;
public MefControllerFactory()
{
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(
new AssemblyCatalog(
Assembly.GetExecutingAssembly()));_Container = new CompositionContainer(catalog);
}
public override IController CreateController(
RequestContext requestContext, string controllerName)
{
return _Container.GetExportedValue(
controllerName);
}
}
Listing 12: MEF extension method
public static object GetExportedValueByType( this CompositionContainer container, Type type)
{
foreach (var PartDef in container.Catalog.Parts)
{
foreach (var ExportDef in PartDef.ExportDefinitions)
{
if (ExportDef.ContractName == type.FullName)
{
var contract = AttributedModelServices.
GetContractName(type);
var definition =
new ContractBasedImportDefinition(
contract, contract, null,
ImportCardinality.ExactlyOne,
-
7/30/2019 CODE Magazine - Article_ Understanding Dependency Injection and Those Pesky Containers
20/20
9/2/13 CODE Magazine - Article: Understanding Dependency Injection and Those Pesky Containers
a se, a se, rea on o cy. ny ;
return container.GetExports(definition).
FirstOrDefault().Value;
}
}
}
return null;
}
Listing 13: WebForms Page Handler Factory
public class MefPageHandlerFactory : PageHandlerFactory
{
public override IHttpHandler GetHandler(
HttpContext context,
string requestType,
string virtualPath,
string path)
{
Page page = base.GetHandler(
context, requestType, virtualPath, path)
as Page;
if (page == null) return page;
CompositionContainer container =
context.Application["Container"]
as CompositionContainer;
container.SatisfyImportsOnce(page);
return page;
}
}