Saturday, June 14, 2014

Unit Test Object Builder – Enter the Expando!

Whilst reading the rather brilliant book Growing Object-Oriented Software, Guided By Tests, I saw an interesting example of using a builder pattern for creating object instances. Here’s a example:

Customer customer = new CustomerBuilder().WithFirstName(“John”).Build();

A builder object can take care of initializing object instances, or even create a list of those object instances. Take a look at nBuilder for an example of a great framework that does just that. What caught me out was the method “WithFirstName”. Interesting; the property “FirstName” appears in the method name. Previously I had seen objects initialized similar to the following

Customer customer = Builder<Customer>.CreateNew().With(c => c.FirstName = "John").Build();

This got me thinking – having just played around with ExpandoObject I decided to have a got at replicating the CustomerBuilder seen in the Growing Objects book. Here’s my attempt:

public static class ObjectChauffeur
{
    public static dynamic Create<T>()
    {
        var result = new ExpandoObject() as IDictionary<string, object>;

        var targetType = typeof(T);

        var targetInstance = Activator.CreateInstance<T>();

        PropertyInfo[] properties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (var prop in properties)
        {
            Func<object, object> targetAction = (param) =>
            {
                prop.SetValue(targetInstance, param, null);

                return result;
            };

            result.Add(string.Concat("With", prop.Name), targetAction);
        }

        CreateBuildFunciton(result, targetInstance);

        return result;
    }

    private static void CreateBuildFunciton<T>(IDictionary<string, object> expando, T instance)
    {
        Func<T> buildFunc = () => instance;

        expando.Add("Build", buildFunc);
    }
}

Before I explain what’s going on, this sample illustrates how to use the above class. First, let’s create a Person object:

public class Person
{
    public int Age { get; set; }

    public string FirstName { get; set; }

    public IList<int> Numbers { get; set; }

    public string Surname { get; set; }

    public override string ToString()
    {
        return string.Concat(this.FirstName, " ", this.Surname, " is ", this.Age);
    }
}

Now, let’s get the chauffer to build an instance of Person:

Person person = ObjectChauffeur.Create<Person>()
    .WithFirstName("Jason")
    .WithSurname("Evans")
    .WithAge(36)
    .WithNumbers(new List<int>() { 1, 2 })
    .Build();

Review the ObjectChauffer class and, specifically, look at the method Create<T>(). It basically uses reflection to iterate through all the public properties of the type T. For each property name the word “With” is prepended. Finally, since ExpandoObject implements IDicitonary<string, object>, we can add the property name and it’s value to an internal dictionary that is returned as the dynamic object.

Admittedly this isn’t the most practical “helper” class ever built, certainly the lack of intellisense when you start typing “With”, and not knowing property names of the target type off hand, will be annoying. But hey, I thought it was an interesting experiment at least.

Saturday, June 07, 2014

Access XML data using ExpandoObject

I haven’t dabbled much with any of the dynamic features available in the .NET Framework, but I recently came across this very handy technique of using ExpandoObject to iterate, and access, XML data a lot cleaner. Let’s use the following XML

<?xml version="1.0" encoding="utf-8" ?>
<Employees>
  <Employee>
    <FirstName>Jason</FirstName>
  </Employee>
  <Employee>
    <FirstName>Mark</FirstName>
  </Employee>
</Employees>

Using an XDocument, we would need to write the following code to iterate through each employee

var doc = XDocument.Load("XmlFile1.xml");

foreach (var element in doc.Element("Employees").Elements("Employee"))
    Console.WriteLine(element.Element("FirstName").Value;

This is how I, and I assume many other devs, have gone about accessing XML data inside an XDocument. So here’s a neater way – an extension method that will do the hard work for us

public static class ExpandoXml
{
    public static dynamic AsExpando(this XDocument document)
    {
        return CreateExpando(document.Root);
    }

    private static dynamic CreateExpando(XElement element)
    {
        var result = new ExpandoObject() as IDictionary<string, object>;

        if (element.Elements().Any(e => e.HasElements))
        {
            var list = new List<ExpandoObject>();

            result.Add(element.Name.ToString(), list);

            foreach (var childElement in element.Elements())
                list.Add(CreateExpando(childElement));
        }
        else
        {
            foreach (var leafElement in element.Elements())
                result.Add(leafElement.Name.ToString(), leafElement.Value);
        }

        return result;
    }
}

Taking in an XDocument, the extension method will iterate through all the XElements, and each child element of those, building up a hierarchy of ExpandoObjects. This provides us the ability to access the XML like so

 var doc = XDocument.Load("XmlFile1.xml").AsExpando();

 foreach (var employee in doc.Employees)
     Console.WriteLine(employee.FirstName);

That looks much better.