Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Utilizing Microsoft's Object Builder to Create Custom Objects Based on the Dependency Injection Patt

0.00/5 (No votes)
24 Apr 2007 1  
An article describing the utilization of the Object Builder framework to develop custom objects for dependency injection.

Screenshot - object_builder_intro1.jpg

Introduction

While trying out the Microsoft Data Access application block for the NET 2.0 framework, I stumbled upon the Object Builder library. I liked the relative ease of a loosely coupled data access functionality of the Database Factory in the Data Access Block so I decided to see what made it tick. To my dismay, there was a miniscule amount of documentation on the subject of what the Object Builder used in it to create database objects for common operations. I did find a good web cast on MSDN that provided sample code for creating a container. Much of this article is based on this code. The Object Builder is the workhorse behind this. The Object Builder is a framework for implementing the Dependency Injection pattern. Think of Dependency Injection in abstract terms like this, using this pattern would allow you to bake a batch of cookies and determine what kind they were going to be when they were already done by injecting, chocolate chips, walnuts, etc. You can read more about the Dependency Injection pattern here.

This article is intended as the fast food version of using dependency injection with Microsoft's Object Builder which is part of the core assembly in Microsoft's Enterprise Library. The Object builder itself is not a class library that can be used by itself to create and inject objects into other objects. It is, however, a framework for building containers for dependency injection. A container is responsible for actually supplying the configuration for how the object will be built. You can specify strategies for creating an object. This demo uses a type mapping strategy; the object type is defined in an XML file (app.config).

Dependency Injection can be used when you have some type of implementation that may change in the future. This could be: business rules, a file format, or any other object that might change frequently and thus cause you headaches. Using this pattern you can create a separate assembly with a different implementation and inject it into your objects without having to recompile the code.

There are a multitude of instances where Dependency Injection would be useful. This demo illustrates using it in an environment where business rules change and the application must support theses changes. The Dependency Injection pattern helps us to extend existing objects without a need to recompile the whole application. This gives us a great deal of flexibility - one example is the Data Access Application Block as part of the Microsoft's Enterprise Library. Dependency Injection is used to allow for generic data access objects to be created, and configured through use of the application configuration file. The interface for the database is the same; for instance, Connect, Commit, Rollback, but the implementation can vary depending on the database Oracle, SQL Server, MySQL, etc .

Background

The Dependency Injection pattern is described in detail by Martin Fowler on his website.

Using the code

Required Resources

  1. Microsoft .NET Framework 3.0
  2. Microsoft Guidance Automation Framework
  3. Microsoft Enterprise Library (I used April 2007 Release, but Jan 2006 Release works as well)
  4. The Redmond Patterns and Practices Web Cast sample container code for Object Builder (I have included this code in my demo but you may download it here)

The scenario for the demo is a drive through for a fast food restaurant. The drive through repeats your order. When the restaurant started there was only one food item you could order, a hamburger with your choice of sauce. Now the restaurant wants to offer its "Fat Burger" with BBQ sauce without changing the code, but some locations don't offer the "Fat Burger" so this item must be configurable for these locations.

The code consists of the Object Builder Container from the Microsoft Patterns and Practices sample, a custom configuration handler that allows the container to be set up in the app.config file, an IFoodItem interface, a hamburger class, a fatburger class, an order class, a winform representing the drive through, and an XSD to validate the Container elements called BuilderXmlConfigElement.XSD (Note: This is included in the source under a folder called Configuration. If you augment the code you must make sure that your BuilderXMLConfig class points to this XSD in the ParseXmlConfiguration Method).

The Container (From the patterns and practices web cast)

First let me try to explain the container without getting too drawn out. You can think of the container as a factory for an object that you would like to inject. It can control the lifetime of the object, and see whether it is a Singleton or not. It can also provide parameters for the constructor or other methods, just to name a few. There are different strategies you can use to set up the container. The demo uses a Type Mapping strategy which means a rule stating if I request an IFoodItem to give me a hamburger object. This rule is set up in the app.config in the demo. The container class DependencyContainer is a class that allows the configuration of the object to be created to be set up by an XML string passed to the constructor. The container elements are constrained by a schema definition file that makes sure that the container contains the conforming elements that the Object Builder will understand and thus be able to utilize the custom container to create your object.

Because the container has a constructor that takes an XML string representing your type mapping strategy, all you have to do is pass a valid XML string that will conform to the XSD. I have created a custom configuration handler so I could include the configuration in the app.config that could then be used to create a valid XML string to send to the container (DependencyContainer class) constructor.

The IConfigurationSectionHandler only requires one override in order to implement. This override is the Create method. Because I wanted this configuration section handler to basically give me an object with an XML string, I also overrode the ToString() method of System.Object so that calling it would return my XML string (Note: the Dependency Container Code actually does the validation of the XML string so it is not necessary in the configuration section handler code, but you could just as easily create a strongly typed configuration handler that validated the objects XML using the BuilderXMLConfigElement.XSD).

//This serves to create an XML string we can pass to our Dependency Container

public object Create(object parent, object configContext, 
    System.Xml.XmlNode section)
{
    XmlConfiguration config = new XmlConfiguration();
    config._xml = "<containerconfig xmlns="container-config"> ";
    config._xml = config._xml + section.InnerXml.Trim();            
    System.Xml.XmlNodeReader oReader = new System.Xml.XmlNodeReader(section);
    config._xml = config._xml + oReader.ReadInnerXml();
    config._xml = config._xml + "</containerconfig>";
    System.Diagnostics.Debug.WriteLine(config._xml);  
    return config;
}
//We override the ToString() method to return an XML string that represents 

//our container configuration.

public override string ToString()
{
    return _xml;
}
#endregion

Our Drive Through and Food Items

Our app.config file will contain the parameters for the dependency, and will be represented by a section called BurgerConfig, which will be handled by the handler described above.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name='BurgerConfig' type='ObjectBuilderDemo.XmlConfiguration, 
      ObjectBuilderDemo,Version=1.0.0.0, Culture=neutral, 
      PublicKeyToken=null'/>    
  </configSections>
  <BurgerConfig>
    <Mappings>
      <Mapping FromType='ObjectBuilderDemo.IFoodItem, ObjectBuilderDemo, 
        Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 
        ToType='ObjectBuilderDemo.Hamburger, ObjectBuilderDemo, 
        Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' />
    </Mappings>
    <BuildRules>
      <BuildRule Type='ObjectBuilderDemo.Hamburger, ObjectBuilderDemo, 
        Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 
        Mode='Instance'>
      <Constructor>
        <Value Type='System.String'></Value>       
      </Constructor>
     </BuildRule>
    </BuildRules>
  </BurgerConfig>
</configuration>

We have defined a mapping, and build rules in our app.config. The mapping states that we want our container to create a Hamburger object when we request the IFoodItem. Our BuildRules collection states a BuildRule for our Hamburger object which is assigned a Constructor BuildRule that has a parameter of type String. This parameter provides the sauce parameter to the constructor of our burger objects. (Remember they get to choose the sauce.)

Our IFoodItem interface contains a Method called GetIngredients() which returns a list of strings containing the ingredients of the implementation.

public interface IFoodItem
{
    System.Collections.Generic.IList<string> GetIngredients();
    string GetSauce();

}

Our implementations are as follows. For the hamburger:

public IList GetIngredients()
{
    burgerIngredients = new System.Collections.Generic.List<string>();
    burgerIngredients.Add("Two all beef patties");
    burgerIngredients.Add(GetSauce());
    burgerIngredients.Add("Lettuce");
    burgerIngredients.Add("Cheese");
    burgerIngredients.Add("Pickles");
    burgerIngredients.Add("Onions");
    burgerIngredients.Add("Sesame Seed Bun");

    return burgerIngredients;

}

With a method called GetSauce() to get the sauce string passed to the constructor.

public Hamburger(string sauce)
{
    if(sauce==null)
        _sauce = "Special Sauce";
    else
        _sauce = sauce;
}

For our Fatburger we have:

public IList GetIngredients()
{
    burgerIngredients = new System.Collections.Generic.List<string>();
    burgerIngredients.Add("Three all beef patties");
    burgerIngredients.Add("Slice of Ham");
    burgerIngredients.Add(GetSauce());           
    burgerIngredients.Add("Lettuce");
    burgerIngredients.Add("Swiss Cheese");
    burgerIngredients.Add("American Cheese");
    burgerIngredients.Add("Bacon");
    burgerIngredients.Add("Pickles");
    burgerIngredients.Add("Relish");           
    burgerIngredients.Add("Onion Rings");
    burgerIngredients.Add("Mushrooms");
    burgerIngredients.Add("Sesame Seed Bun");
    return burgerIngredients;

}

We have a DriveThrough class that contains a constructor that takes a IFoodItem argument and contains a Order property.

public DriveThrough(IFoodItem burger)
{
    _burger = burger;
}

public string Order
{
    get
    {
        {
            string returnValue="";
            foreach (string ingredient in _burger.GetIngredients())
            {
                returnValue = returnValue + ingredient + 
                    System.Environment.NewLine;
            }
            return returnValue;
        }
    }

}

Finally we have the form with an event handler for the button click that Reads the configuration section handler for the BurgerConfig, uses the container to return the appropriate burger by passing the DependencyContainer the XML string from the app.config, creates a DriveThrough object and passes our injected burger as a constructor argument, and finally displays a message box with the order.

private void btnGreet_Click(object sender, EventArgs e)
{          
    DriveThrough driveThrough; 
    //The container takes an XML string argument

    XmlConfiguration o = 
      (XmlConfiguration)System.Configuration.ConfigurationManager.GetSection(
      "BurgerConfig");  
    //We pass the XML string from our Configuration file to the constructor of 

    //the DependencyContainer

    DependencyContainer container = new DependencyContainer(o.ToString());   
    //We will get the approriate implementation of the burge back as defined 

    //in the app.config

    ObjectBuilderDemo.IFoodItem food = container.Get();
    //we pass our injected object into the DriveThrough

    driveThrough = new DriveThrough(food);
    //read the order ingerdients

    MessageBox.Show(driveThrough.Order);
}

If we run the application, our order is repeated using the ingredients from the hamburger. Let assume now we want to start selling our Fatburger, but we only want to offer it with BBQ Sauce. It's simple, all we have to do is re-define the mapping in our app.config and pass BBQ Sauce as our constructor parameter. See below:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name='BurgerConfig' type='ObjectBuilderDemo.XmlConfiguration, 
    ObjectBuilderDemo,Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'/>
  </configSections>
  <BurgerConfig>
    <Mappings>
      <Mapping FromType='ObjectBuilderDemo.IFoodItem, ObjectBuilderDemo, 
        Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 
        ToType='ObjectBuilderDemo.Fatburger, ObjectBuilderDemo, 
        Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' />
    </Mappings>
    <BuildRules>
      <BuildRule Type='ObjectBuilderDemo.Fatburger, ObjectBuilderDemo, 
        Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 
        Mode='Instance'>
      <Constructor>
        <Value Type='System.String'>BBQ Sauce</Value>       
      </Constructor>
     </BuildRule>
    </BuildRules>
  </BurgerConfig>
</configuration> 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here