Using Spring .NET Framework IoC with List

Using the Spring.NET Inversion of Control Container

The Spring .NET Framework provides a highly modularized way of adding best practices and reducing the complexity of application. It is conceptually based on the Spring Framework for Java(R). Using various modules Spring.NET provides support for Inversion of Control/Dependency Injection, Aspected Oriented Programming, Messaging, Web Service, Testing, Scheduling among other things. One of the core technologies that it provides is Inverson of Control (IoC). This article will look at creating an application in Visual Studio 2012 that configures objects in an XML file and then uses the Spring IoC container to instantiate those objects. To make it more interesting, the object in question will contain a generic collection. Spring.NET is independent of the type of application, so for the purposes of this article a WPF Application is chosen.

Starting the CardGameApp

1. Use Visual Studio 2012 to create a new WPF Application named CardGameApp.
To make use of the Spring.NET IoC container add the Spring.Core library to the reference. This can be done manually, or as in this step, but selecting Manage NuGet Packages.

2. Right-click on the CardGameApp project in Solution Explorer and select Manage NuGet Package…

3. Search online for Spring.Core and Install.
The configuration information for Spring.NET can be stored in a number of different locations, but one of the most common places to configure the application is the XML files, such as App.Config.

Configure the Application Objects

4. Add a New Item to the project of type Application Configuration File, and name it App.config.

It would be helpful to group Spring.NET configuration settings into a section, and within the section define the ContextHandler and DefaultSectionHandler information. These sections are named, and the Type referenced by full name, and assembly name, for example type=”NamespaceName.ClassName, AssemblyName”. Add the spring-objects-1.3.xsd schema to the app.config schemas property.

5. Add the following <configSection/> information to the App.config within the <configuration/> element.

<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler,
Spring.Core"/>
</sectionGroup>
</configSections>

The concept for the application is a CardDeck object that contains a collection of Card objects. The number of cards in each of the four suits can be defined using a constructor for CardDeck.

6. After the close of the <configSections/> element, add the <spring/> section group, and define the <context/> and <objects/> sections within.

<spring xmlns="http://www.springframework.net">
<context>
</context>
<objects>
</objects>
</spring>

The context defines where the IoC container should look for configuration information. In this case the information is stored in the configuration data under the spring section group, in the section named objects. Set this as the resource uri for the context. Note, the schema for the spring framework should be applied to the file.

7. Expand on the <context/> element to include a <resource/> element with the uri of config://spring/objects. Name the <context/> element CardGameContext to make retrieval easier.

<context name="CardGameContext" >
<resource uri="config://spring/objects"/>
</context>

Define the Implementation

The objects that need to be created can now be defined within the <objects/> element. Consider the constructor and property elements that will be required as each card is created, or initialized. For this article, the Card will not require any constructor arguments, but expose the Rank and Suit properties. This makes the configuration information relatively easy. The <object/> element should have an identifier which is usually the id attribute. It also needs to be told the type of object to create. As earlier the Type needs to have the full type name, and the assembly information provided.

8. Insert into the <objects/> element a new <object/> element describing the Card objects.

<objects>
<object id="aCard" type="CardGameApp.Card, CardGameApp"/>
</objects/>

Now an actual public Card class needs to be defined that the configuration can access.

9. Add a new class to the project named Card. Add public properties for Rank and Suite. Rank should be an integer between 1 and 13, while the Suit character from the set C, H, D, S.

The next step is to create and configure the CardDeck.

10. Start by defining a new public class named CardDeck that in its constructor accepts the number of cards in the deck, and the name of the game. These can be exposed to the application as readonly properties named CardCount as an integer, and GameName as a string. Also expose an IList of Card objects as the Deck property.

Instantiate the Objects

Now set the configuration to create an instance of CardDeck. This time the object contains some constructor configuration. The <constructor-arg/> element can be used to pass in constructor arguments.

11. Add a second <object/> element to the <objects/> in the configuration file.

<objects>
...
<object id="aCardDeck" type="CardGameApp.CardDeck, CardGameApp" >
<constructor-arg name="cardCount" value="52"/>
<constructor-arg name="gameName" value="Bridge"/>
</object>
</objects>

To test the progress so far, the XmlApplicationContext object can be called at the startup of the application to request instances of CardDeck, and Card. For this WPF application, add an instance of IApplicationContext to the App class, and override the OnStartup method to populate the context. The ContextRegistry class provides a GetContext method that retrieves the context for the current application configuration by the name of the context.

12. Retrieve the context, create an instance of CardDeck and Card and write to the Trace output the result.

IApplicationContext ctx = ContextRegistry.GetContext("CardGameContext");
// Testing
CardDeck cd = (CardDeck)ctx.GetObject("aCardDeck");
Card c = (Card)ctx.GetObject("aCard");
System.Diagnostics.Trace.WriteLine(cd);
System.Diagnostics.Trace.WriteLine(c);

Modify the configuration to create an instance of System.Collection.Generic.List<> for Card objects and assign this to the Deck property of the CardDeck; The <list/> element can be used to define a list of values. If adding the <list/> element causes namespace problems. Add the xmlns=”http://www.springframework.net” to the <objects/> element to eliminate this problem.

13. Configure a <property/> element for the CardDeck to assign a List<Card> to the Deck property.

<property name="Deck">
<list element-type="CardGameApp.Card, CardGameApp">
<object type="CardGameApp.Card, CardGameApp">
<property Name="Rank" Value="11"/>
<property Name="Suit" Value="H"/>
</object
<object type="CardGameApp.Card, CardGameApp">
<property Name="Rank" Value="1"/>
<property Name="Suit" Value="D"/>
</object
<object type="CardGameApp.Card, CardGameApp">
<property Name="Rank" Value="12"/>
<property Name="Suit" Value="S"/>
</object
...

<object type="CardGameApp.Card, CardGameApp">
<property Name="Rank" Value="12"/>
<property Name="Suit" Value="C"/>
</object
<object type="CardGameApp.Card, CardGameApp">
<property Name="Rank" Value="4"/>
<property Name="Suit" Value="H"/>
</object
<object type="CardGameApp.Card, CardGameApp">
<property Name="Rank" Value="5"/>
<property Name="Suit" Value="H"/>
</object
</list>
</property>

The final test is to loop through the newly created CardDeck to confirm the cards were added.

14. Add a foreach loop to check the Deck property for contents.

IApplicationContext ctx = ContextRegistry.GetContext("CardGameContext");
// Testing
CardDeck cd = (CardDeck)ctx.GetObject("aCardDeck");
foreach (Card c in cd.Deck)
{
System.Diagnostics.Trace.WriteLine(string.Format("{1}:{0}", c.Rank, c.Suit));
}

By using the Spring.NET core configuration technology, this CardDeck object has been instantiated and populated based on configuration settings. This will make it easy to modify in support of other card games.