Introduction
The code presented here allows the use of XAML to implement the Inversion of Control pattern (IoC, also known as Dependency Injection). Using IoC with XAML allows us to write lowly-coupled applications that are easy to test. The code given here was constructed in Visual Studio 2005 using the November 2006 CTP of WPF.
Background
Martin Fowler gives an excellent description of the IoC pattern here. At its simplest, IoC is a pattern where a component gets its dependencies injected from the outside (usually by a container) instead of having the dependencies hard-coded within the component. The main advantage of IoC is that your components are decoupled from their dependencies. That makes them much easier to move around and hence reuse them, as well as unit-test them.
A way of seeing the IoC pattern is to see it as an implementation of the factory pattern. The dependencies of a component are wired into it by an object factory. Most IoC frameworks allow this wiring to take place declaratively instead of coding a factory class per component in an imperative language (e.g. C#).
IoC is a pattern mostly used in the Java community where the Spring Framework, implementing IoC (among other things), is very pervasive. The framework was partially ported to .NET in the Spring.NET Framework and since then, IoC has been catching some speed in the .NET community.
The XAML language, part of .NET Framework 3.0, offers an alternative to using IoC. There is very little to add to XAML to obtain usable IoC that is type-checked by the compiler. XAML is an instantiation language: its constructs describe how to instantiate different objects and setting dependencies. XAML is now part of the .NET Framework and is therefore going to be known by a majority of .NET developers by the end of 2007. This makes XAML a low-entry-level candidate for implementing IoC.
XAML is based on XML and is therefore declarative by nature. That makes it a good candidate for IoC. The declarative power of XAML has already been recognized by many groups, for instance, the MyXAML open-source project.
Using the Code
The main example I'm going to show is a classic IoC. The following UML diagram shows the main classes (the colour code separates application classes from the mini-IoC Framework we provide). PersonValidator
is a component with two dependencies: an alert service and a persistence service. Both of those dependencies are modeled as interfaces. Both interfaces have one implementation shown here, but they could have others. The main point is that PersonValidator
only depends on the interfaces, not on their implementation. Something else knows about the interface implementation and wires them into the component and that is XAML.
The C# code using the component is the following:
private static void TestPersonValidation()
{
PersonValidator validator = ObjectFactory.GetObject<
PersonValidator>(typeof(PersonValidator).FullName);
validator.ValidatePersons(42, 12, 79, 14);
}
The validator
instance isn't instantiated by new
, but by an object factory. ObjectFactory
is part of the code we added and joined to this article to make XAML IoC-friendly. We will come back to what it exactly does later. For this section, it suffices to know that ObjectFactory
goes into a XAML dictionary and finds a resource identified with the Key corresponding to the value of MyLib.PersonValidator ( this is the string returned by typeof(PersonValidator).FullName
).
The validator
instance comes all configured with its persistence and alert services and ready to perform its ValidatePersons
method. Let's look at how this configuration happened. The XAML code responsible for this configuration is inside the file TestPersonValidation.xaml
:
<my:PersonValidator x:Shared="false"
x:Key="MyLib.PersonValidator">
<my:PersonValidator.PersistenceService>
<my:ResourcePersistenceService/>
</my:PersonValidator.PersistenceService>
<my:PersonValidator.AlertService>
<my:ConsoleAlertService/>
</my:PersonValidator.AlertService>
</my:PersonValidator>
This configuration is contained inside a ResourceDictionary
which is a Windows Presentation Foundation (WPF) class containing different entries. Each entry is an object instantiation description: a recipe to create an object.
The type of the object is defined by the XML element: my:PersonValidator
. The XML prefix my
is mapped to a CLR namespace by the XAML namespace declaration: xmlns:my="clr-namespace:MyLib;assembly=MyLib"
. We will explain the x:Shared
attribute in a following section. x:Key
is the key of the object in the dictionary.
Both service properties are declared in what is known as XAML property element syntax. Both are simple declarations of the type of the implementation of each interface. This XAML could have been written in the more compact format using markup extensions:
<my:PersonValidator x:Shared="false" x:Key="
MyLib.PersonValidator"
PersistenceService="{x:Type
my:ResourcePersistenceService}"
AlertService="{x:Type my:ConsoleAlertService}"/>
Points of Interest
There are several points of interest about how to make XAML IoC friendly. We raise those points here and show how we addressed them. The library augmenting XAML (also shipped with this article) is called XamlIoc
and has the XML namespace ioc
in XAML (in the following examples).
XAML outside WPF
XAML was primarily designed to work with Windows Presentation Foundation (WPF). Visual Studio 2007 (codename Orcas) XAML designer (codename cider) is also expecting WPF objects. Currently, there are quite a few glitches when working with XAML with objects other than out-of-the-box WPF objects. Those glitches should be fixed by the time Visual Studio 2007 goes into release (sometime in 2007).
The major problem is that classes referenced in a XAML file within a project must be defined outside that project. This actually causes a compilation error. This is why we've created the MyLib
project in the sample code.
Another problem is that even if you refer to classes compiled outside the project, the designer raises warnings and errors. This seems to be strictly a designer issue since the project does compile.
Yet another problem: when you refer to other CLR namespaces and assemblies in an XML namespace declaration, the designer complains that it cannot find your namespaces in your assembly. Again, that seems to be a designer issue only; compilation goes fine.
Those three problems also affect development in WPF, so we are confident that they will be addressed before the product goes into release.
Default Constructors
Actually, this is a limitation of XAML that we didn't try to get around. A class must have a default constructor to be used in XAML. Data is passed to the class strictly by properties.
Resource Dictionary
We use resource dictionaries to store the object instantiation description. How does ObjectFactory
know where to find the dictionary? It must be set up in the application configuration file (or web.config for web applications):
="1.0" ="utf-8"
<configuration>
<configSections>
<section name="iocConfigSection"
type="XamlIoc.Configuration.IocConfigSection,
XamlIoc"/>
</configSections>
<iocConfigSection mainContainer="Container.xaml">
<standAloneResources>
-->
</standAloneResources>
</iocConfigSection>
</configuration>
The attribute mainContainer
on iocConfigSection
specifies the file containing the resource dictionary. This file must have a resource dictionary as the root element. If no file is specified, ObjectFactory
uses the resource dictionary of the WPF-Application if there is one. This is a neat feature, since it allows the sharing of object descriptions between WPF-objects (e.g. Windows) and objects calling ObjectFactory
explicitly.
The standAloneResources element isn't used in the code sample. It is useful if you want to have other XAML files defining objects. Those files are standalone and aren't resource dictionaries. As mentioned above, ResourceDictionary
is a WPF class. This class comes with a nice feature: MergedDictionaries
. This is a property where you can specify child-dictionary. This allows you to spread your object descriptions in multiple files.
<wpf:ResourceDictionary
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ioc="clr-namespace:XamlIoc;assembly=XamlIoc"
xmlns:my="clr-namespace:MyLib;assembly=MyLib"
>
<wpf:ResourceDictionary.MergedDictionaries>
<wpf:ResourceDictionary Source="TestPersonValidation.xaml"/>
<wpf:ResourceDictionary Source="TestShared.xaml"/>
<wpf:ResourceDictionary Source="TestGenerics.xaml"/>
</wpf:ResourceDictionary.MergedDictionaries>
</wpf:ResourceDictionary>
x:Shared
A little-known XAML attribute is x:Shared
. In a resource dictionary item, it tells the Framework if a new object should be created every time the resource is queried or if the same instance should be shared. This allows the creation of singletons in the application. By default, all the instances are shared. This is why in WPF, by default, all application-wide resources are singletons. It avoids the creation of an object every time the resource is queried.
This is all nice and well in WPF where you typically put styles and brushes in the application resources. But for IoC, you'll probably want to create a new object every time. For this reason, we recommend that you use x:Shared="false"
when you do not want singleton. Shared resources are created on demand (lazy-init).
StaticResource
The standard way to refer to resources within another resource is the use of either StaticResource
or DynamicResource
XAML markup extensions. DynamicResource
uses a mechanic called DependencyProperty
. It isn't useful for IoC, since it would put the constraint on how to build the objects. StaticResource
has one known limitation: even if the resource referenced is tagged as x:Shared="false"
, the resource is instantiated only once.
This means that if we only used out-of-the-box XAML, we wouldn't be able to assemble objects from multiple object descriptions. This would severely limit us in terms of scaling the object descriptions, since we would have to duplicate object descriptions instead of reusing them.
For this reason, we introduced another markup extension: ObjectFactoryExtension
. It is used the same way as StaticResource
or DynamicResource
, but calls ObjectFactory
to query the resource. This respects the x:Shared="false"
statement. A usage example of ObjectFactoryExtension
:
<wpf:ResourceDictionary
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ioc="clr-namespace:XamlIoc;assembly=XamlIoc"
xmlns:my="clr-namespace:MyLib;assembly=MyLib"
>
-->
<my:Address x:Shared="false" x:Key="
protoAddress"/>
<my:Person x:Shared="false"
x:Key="objectFactoryPerson" my:Address="
{ioc:ObjectFactory protoAddress}"/>
<my:Person x:Shared="false" x:Key="
staticResourcePerson" my:Address="{
wpf:StaticResource protoAddress}"/>
</wpf:ResourceDictionary>
Here we have an address that is defined as a non-shared resource and two persons using that address description. The first person, objectFactoryPerson
, uses ObjectFactoryExtension
while the second, staticResourcePerson
, doesn't. The following code tests both person object descriptions:
private static void TestShared()
{
Person objectFactoryPerson1 = ObjectFactory.GetObject
<Person>("objectFactoryPerson");
Person objectFactoryPerson2 = ObjectFactory.GetObject
<Person>("objectFactoryPerson");
Person staticResourcePerson1 = ObjectFactory.GetObject
<Person>("staticResourcePerson");
Person staticResourcePerson2 = ObjectFactory.GetObject
<Person>("staticResourcePerson");
Console.WriteLine(
"object factory persons equal: "
+ object.ReferenceEquals(objectFactoryPerson1,
objectFactoryPerson2));
Console.WriteLine(
"static resource persons equal: "
+ object.ReferenceEquals(staticResourcePerson1,
staticResourcePerson2));
Console.WriteLine(
"object factory addresses equal: "
+ object.ReferenceEquals(objectFactoryPerson1
.Address, objectFactoryPerson2.Address));
Console.WriteLine(
"static resource addresses equal: "
+ object.ReferenceEquals(staticResourcePerson1
.Address, staticResourcePerson2.Address));
}
The main test is the last line of code. In the case where the person object description uses StaticResource
, the reference address is the same reference every time the person object is created. Of course, we could use StaticResource
when we do not mind singletons, but for consistency we suggest to always use ObjectFactoryExtension
to refer to another resource.
Generics
Generics are not supported by XAML. Actually, they are supported in the a very special case with the x:TypeArguments
markup extension. The object defined needs to be at the root of a XAML file and using the x:Class
markup extension, which defines a class deriving from the defined object class. This support isn't good for us in general, since it would force us to derive a class every time we want to use it.
We derived a solution to this problem from one of Mike Hillberg's Blog posts on WPF. We created a GenericExtension
capable of instantiating any generic class and also setting properties on it. For example:
<ioc:ObjectContainer x:Key="myListOfInt">
<ioc:Generic
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:sysGenCol="clr-namespace:System.Collections.Generic;assembly=mscorlib"
ioc:TypeName="sysGenCol:List"
FirstTypeArgument="{x:Type TypeName=sys:Int32}">
<ioc:Generic.PropertyDictionary>
<sys:Int32 x:Key="Capacity">12</sys:Int32>
</ioc:Generic.PropertyDictionary>
</ioc:Generic>
</ioc:ObjectContainer>
Unfortunately, the solution is a bit convoluted.
First, XAML doesn't see markup extensions as markup extensions when they are at the root of a resource dictionary element: it sees it as if we would want instantiate the markup extension object. For that reason, we had to create an ObjectContainer
containing GenericExtension
. ObjectFactory
knows about ObjectContainer
and returns the child of the container.
Second, since we use the markup extension to create the type, we do not have the object we want to create at hand and we cannot set properties on it. GenericExtension
has the property PropertyDictionary
that accepts a key-value pair property name/value. This does work. In the example, we set the property Capacity
to 12
. The problem is that this is done through reflection and hence we loose a nice feature of XAML: static compiling check.
Conclusion
With little effort, XAML offers a nice alternative for using IoC in .NET applications. IoC helps to design loosely coupled components and can therefore help greatly to create manageable large-scale applications.
History
- 17 January, 2007 -- Original version posted