Chapter 5. The IObjectWrapper and Type conversion

5.1. Introduction

(Available in 1.0)

The concepts encapsulated by the IObjectWrapper interface are fundamental to the workings of the core Spring.NET libraries The typical application developer most probably will not ever have the need to use the IObjectWrapper directly... because this is reference documentation however, we felt that some explanation of this core interface might be right. The IObjectWrapper is explained in this chapter since if you were going to use it at all, you would probably do that when trying to bind data to objects, which, nicely enough, is precisely the area that the IObjectWrapper addresses.

5.2. Manipulating objects using the IObjectWrapper

One quite important concept of the Spring.Objects namespace is encapsulated in the definition IObjectWrapper interface and its corresponding implementation, the ObjectWrapper class. The functionality offered by the IObjectWrapper includes methods to set and get property values (either individually or in bulk), get property descriptors (instances of the System.Reflection.PropertyInfo class), and to query the readability and writability of properties. The IObjectWrapper also offers support for nested properties, enabling the setting of properties on subproperties to an unlimited depth. The IObjectWrapper usually isn't used by application code directly, but by framework classes such as the various IObjectFactory implementations.

The way the IObjectWrapper works is partly indicated by its name: it wraps an object to perform actions on a wrapped object instance... such actions would include the setting and getting of properties exposed on the wrapped object.

Note: the concepts explained in this section are not important to you if you're not planning to work with the IObjectWrapper directly.

5.2.1. Setting and getting basic and nested properties

Setting and getting properties is done using the SetPropertyValue() and GetPropertyValue() methods, for which there are a couple of overloaded variants. The details of the various overloads (including return values and method parameters) are all described in the extensive API documentation supplied as a part of the Spring.NET distribution.

The aforementioned SetPropertyValue() and GetPropertyValue() methods do have a number of conventions for indicating the path of a property. A property path is an expression that implementations of the IObjectWrapper interface can use to look up the properties of the wrapped object; some examples of property paths include...

Table 5.1. Examples of property paths

PathExplanation
nameIndicates the name property of the wrapped object.
account.nameIndicates the nested property name of the account property of the wrapped object.
account[2]Indicates the third element of the account property of the wrapped object. Indexed properties are typically collections such as lists and dictionaries, but can be any class that exposes an indexer.


Below you'll find some examples of working with the IObjectWrapper to get and set properties. Consider the following two classes:

[C#]
public class Company
  {
    private string name;
    private Employee managingDirector;

    public string Name
    {
      get { return this.name; }
      set { this.name = value; }
    }
    
    public Employee ManagingDirector
    {
      get { return this.managingDirector; }
      set { this.managingDirector = value; }
    }
}

[C#]
public class Employee
{
    private string name;
    private float salary;
    
    public string Name
    {
      get { return this.name; }
      set { this.name = value; }
    }

    public float Salary
    {
      get { return salary; }
      set { this.salary = value; }
    }
}

The following code snippets show some examples of how to retrieve and manipulate some of the properties of IObjectWrapper-wrapped Company and Employee instances.

[C#]
Company c = new Company();
IObjectWrapper owComp = new ObjectWrapper(c);
// setting the company name...
owComp.SetPropertyValue("name", "Salina Inc.");
// can also be done like this...
PropertyValue v = new PropertyValue("name", "Salina Inc.");
owComp.SetPropertyValue(v);

// ok, let's create the director and bind it to the company...
Employee don = new Employee();
IObjectWrapper owDon = new ObjectWrapper(don);
owDon.SetPropertyValue("name", "Don Fabrizio");
owComp.SetPropertyValue("managingDirector", don);

// retrieving the salary of the ManagingDirector through the company
float salary = (float)owComp.GetPropertyValue("managingDirector.salary");

Note that since the various Spring.NET libraries are compliant with the Common Language Specification (CLS), the resolution of arbitrary strings to properties, events, classes and such is performed in a case-insensitive fashion. The previous examples were all written in the C# language, which is a case-sensitive language, and yet the Name property of the Employee class was set using the all-lowercase 'name' string identifier. The following example (using the classes defined previously) should serve to illustrate this...

[C#]
// ok, let's create the director and bind it to the company...
Employee don = new Employee();
IObjectWrapper owDon = new ObjectWrapper(don);
owDon.SetPropertyValue("naMe", "Don Fabrizio");
owDon.GetPropertyValue("nAmE"); // gets "Don Fabrizio"

IObjectWrapper owComp = new ObjectWrapper(new Company());
owComp.SetPropertyValue("ManaGINGdirecToR", don);
owComp.SetPropertyValue("mANaGiNgdirector.salARY", 80000);
Console.WriteLine(don.Salary); // puts 80000

The case-insensitivity of the various Spring.NET libraries (dictated by the CLS) is not usually an issue... if you happen to have a class that has a number of properties, events, or methods that differ only by their case, then you might want to consider refactoring your code, since this is generally regarded as poor programming practice.

5.2.2. Other features worth mentioning

In addition to the features described in the preceding sections there a number of features that might be interesting to you, though not worth an entire section.

  • determining readability and writability: using the IsReadable() and IsWritable() methods, you can determine whether or not a property is readable or writable.

  • retrieving PropertyInfo instances: using GetPropertyInfo(string) and GetPropertyInfos() you can retrieve instances of the System.Reflection.PropertyInfo class, that might come in handy sometimes when you need access to the property metadata specific to the object being wrapped.

5.3. Type conversion

If you associate a TypeConverter with the definition of a custom Type using the standard .NET mechanism (see the example code below), Spring.NET will use the associated TypeConverter to do the conversion.

[C#]
[TypeConverter (typeof (FooTypeConverter))]
public class Foo
{
}

The TypeConverter class from the System.ComponentModel namespace of the .NET BCL is used extensively by the various classes in the Spring.Core library, as said class “... provides a unified way of converting types of values to other types, as well as for accessing standard values and subproperties.[4]

For example, a date can be represented in a human readable format (such as 30th August 1984), while we're still able to convert the human readable form to the original date format or (even better) to an instance of the System.DateTime class. This behavior can be achieved by using the standard .NET idiom of decorating a class with the TypeConverterAttribute. Spring.NET also offers another means of associating a TypeConverters with a class. You might want to do this to achieve a conversion that is not possible using standard idiom... for example, the Spring.Core library contains a custom TypeConverter that converts comma-delimited strings to String array instances. Registering custom converters on an IObjectWrapper instance gives the wrapper the knowledge of how to convert properties to the desired Type.

An example of where property conversion is used in Spring.NET is the setting of properties on objects, accomplished using the aforementioned TypeConverters. When mentioning System.String as the value of a property of some object (declared in an XML file for instance), Spring.NET will (if the type of the associated property is System.Type) use the RuntimeTypeConverter class to try to resolve the property value to a Type object. The example below demonstrates this automatic conversion of the Example.Xml.SAXParser (a string) into the corresponding Type instance for use in this factory-style class.

<objects xmlns="http://www.springframework.net">
<object id="parserFactory" type="Example.XmlParserFactory, ExamplesLibrary"
destroy-method="Close">
  <property name="ParserClass" value="Example.Xml.SAXParser, ExamplesLibrary"/>
</object>
</objects>

[C#]
public class XmlParserFactory
{
	private Type parserClass;
	
	public Type ParserClass
	{
	  get { return this.parserClass; }
	  set { this.parserClass = value; }
	}
	
	public XmlParser GetParser ()
	{
	  return Activator.CreateInstance (ParserClass);
	}
}

5.3.1. Type Conversion for Enumerations

The default type converter for enumerations is the System.ComponentModel.EnumConverter class. To specify the value for an enumerated property, simply use the name of the property. For example the TestObject class has a property of the enumerated type FileMode. One of the values for this enumeration is named Create. The following XML fragment shows how to configure this property

<object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests">
  <property name="name" value="Rod"/>
  <property name="FileMode" value="Create"/>
</object>

5.4. Built-in TypeConverters

Spring.NET has a number of built-in TypeConverters to make life easy. Each of those is listed below and they are all located in the Spring.Objects.TypeConverters namespace of the Spring.Core library.

Table 5.2. Built-in TypeConverters

TypeExplanation
RuntimeTypeConverterParses strings representing System.Types to actual System.Types and the other way around.
FileInfoConverterCapable of resolving strings to a System.IO.FileInfo object.
StringArrayConverterCapable of resolving a comma-delimited list of strings to a string-array and vice versa.
UriConverterCapable of resolving a string representation of a URI to an actual URI-object.
FileInfoConverterCapable of resolving a string representation of a FileInfo to an actual FileInfo-object.
StreamConverterCapable of resolving Spring IResource URI (string) to its corresponding InputStream-object.
ResourceConverterCapable of resolving Spring IResource URI (string) to an IResource object.
ResourceManagerConverterCapable of resolving a two part string (resource name, assembly name) to a System.Resources.ResourceManager object.
RGBColorConverterCapable of resolving a comma separated list of Red, Green, Blue integer values to a System.Drawing.Color structure.
RegexConverterConverts string representation of regular expression into an instance of System.Text.RegularExpressions RegEx


Spring.NET uses the standard .NET mechanisms for the resolution of System.Types, including, but not limited to checking any configuration files associated with your application, checking the Global Assembly Cache (GAC), and assembly probing.



[4] More information about creating custom TypeConverter implementations can be found online at Microsoft's MSDN website, by searching for Implementing a Type Converter.