Tip of the Day

Tip of the Day #19: Create a list of objects instead of many lists of values

I?ve been reviewing some code and I came across something that jars. What is wrong with this is many-fold. Essentially, instead of encapsulating an related data into an entity that describes the whole the developer had created silos of data values, and you’d better hope that nothing went awry with any of it.

It looked like this:

List<string> addOnNames = new List<string>();
List<string> addOnDescriptions = new List<string>();
List<string> addOnCodes = new List<string>();
List<decimal> addOnBasePrice = new List<decimal>();

Okay, so if you don’t yet find it jarring, here are some of the things that are wrong with this structure.

To iterate through and operate on a single “add on” you have to do something like this:

for(int i = 0; i < addOnNames.Count; i++)
{
    string name = addOnNames[i];
    string description = addOnDescriptions[i];
    string code = addOnCodes[i];
    decimal basePrice = addOnCode[i];
    // Do stuff that operates on an "Add On" here
}

That?s quite a bit of work just to get at the values you need in a particular loop iteration.

If you need to pass the lists on to methods, you have to pass each in its own parameter. Method signatures start to become needlessly large. In the simplified example above (yes, the real code had a lot more in it) there are three extra parameters that need to be passed around to each method call that needs to act on a collection of add-ons.

private void DoSomethingToACollectionOfAddOns(
                     List<string> addOnNames,
                      List<string> addOnDescriptions,
                      List<string> addOnCodes,
                     List<decimal> addOnBasePrice)
{
    // Do something.
}

It is also quite difficult to use LINQ to query what is effectively an AddOn entity.

Unless you are very careful, your lists can become out of sync, and when that happens all sorts of strange and difficult to trace bugs enter into the system. In the code I was reviewing there was an edge case where by one of the lists didn?t get updated when it was initially populated. Because all the lists are assumed to be the same length the first time that they were iterated over, what should have been the final entity couldn?t be retrieved on one of the lists because it didn?t exist. As this happened well after the creation of the lists and in a method called several levels deep it was quite a job working out where the original bug was coming from.

Solution

What needs to happen here is that an entity class is created for an add on. Each of these lists indicates a field or property in an entity class. The class might simply look something like this:

public class AddOn{
    public string Name { get; set; }
    public string Description { get; set; }
    public string Code { get; set; }
    public decimal BasePrice { get; set; }
}

This encapsulates everything about a single add on entity in one place. If you want a collection of these objects you can do something like this:

    List<AddOn> addOns = new List<AddOn>();

If you want to loop over them you don?t have to write lots of code to get all the elements out of a variety of lists a simple foreach will suffice (unless you need to also know the index)

    foreach(AddOn addOn in addOns){}

If you need to pass the entity around, or a collection of the entities around then you only need to pass one parameter into a method.

    private void DoSomethingToACollectionOfAddOns(List<AddOn> addOns) {}

Using LINQ becomes much easier because now you have everything encapsulated in the one place. I can?t even imagine the convoluted joins that would be needed to process the individual values in a LINQ expression otherwise.

When creating the initial collection, if any particular property is not needed then it can be simply ignored, if need be a default value can be set in the constructor. Then you never have an issue with one collection being out of sync with another. You no longer have to worry about synchronising collections, everything to do with a single instance of the entity is in one place.

Software Development

Monitoring change in XML data (LINQ to XML series – Part 5)

This is the 5th part in a series on LINQ to XML. In this instalment we will look at monitoring changes in XML data in the XML classes added to .NET 3.5.

The XObject class (from which XElement and XAttribute, among others) contains two events that are of interest to anyone wanting to know about changes to the XML data: Changing and Changed

The Changing event is triggered prior to a change being applied to the XML data. The Changed event is triggered after the change has been applied.

An example of adding the event handler would be something like this:

XElement root = new XElement("root");
root.Changed += new EventHandler<XObjectChangeEventArgs>(root_Changed);

The above example will trigger for any change that happens in the node the event handler is applied to and any node downstream of it. As the example is applied to the root node this means the event will trigger for any change in the XML data.

The event handler is supplied an XObjectChangeEventArgs object which contains an ObjectChange property. This is an XObjectChange enum and it lets the code know what type of change happened.

The sender contains the actual object in the XML data that has changed.

Adding an element

Take the following example where an element is added to the XML data.

XElement child = new XElement("ChildElement", "Original Value");
root.Add(child);

In this case the ObjectChanged is Add and the sender is the XElement: <ChildElement>Original Value</ChildElement>

A similar scenario happens when adding an attribute. However, instead of the sender being an XElement it will be an XAttribute.

child.Add(new XAttribute("TheAttribute", "Some Value"));

Changing an element value

If the value of the element is changed (the bit that currently says “Original Value”) then we don’t get one event fired. We get two events fired. For example:

child.Value = "New Value";

The first event with ObjectChanged set to Remove and the sender set to “Orginal Value” (which is actually an XText object) and the second event with the ObjectChanged set to Add and the sender set to “New Value” (again, this is actually an XText object).

Changing an element name

If the name of the element is changed then the ObjectChanged property will be set to Name and the sender will be the XElement that has changed.

child.Name = "JustTheChild";

Changing an attribute name

Unlike changing an element value, when the value of an attribute changes the ObjectChanged property will be Value and the sender will be the XAttribute.

child.Attribute("TheAttribute").Value = "New Attribute Value";

Technorati Tags: ,,,
Misc

Navigating XML (LINQ to XML series – part 4)

In my last few posts on LINQ to XML (part 1, part 2 and part 3) I’ve shown you a starter on navigating around XML data. In this post I’ll continue to show you how to navigate through XML data by showing you how to navigate around sibling elements.

First consider this code:

XElement root = new XElement("root",
    new XElement("FirstChild"),
    new XElement("SecondChild"),
    new XElement("ThirdChild"),
    new XElement("FouthChild"),
    new XElement("FifthChild"));

Which produces the following XML structure:

<root>

<FirstChild />

<SecondChild />

<ThirdChild />

<FouthChild />

<FifthChild />

</root>

We can access the ThirdChild with this code:

XElement child = root.Element("ThirdChild");

From that point, we can also get access to its siblings.

To access the siblings that occur before the element we have a reference to then we can use ElementsBeforeSelf. As with Elements this returns an IEnumerable<XElement> object which allows us to iterate over the result, like this:

IEnumerable<XElement> elements = child.ElementsBeforeSelf();

foreach (XElement element in elements)
    Console.WriteLine(element);

The result is:

<FirstChild />

<SecondChild />

Conversely, we can get the siblings that come after the element we have a reference to with ElementsAfterSelf. Like this:

IEnumerable<XElement> elements = child.ElementsAfterSelf();

The result in this case will be:

<FouthChild />

<FifthChild />

Technorati Tags: ,,,
Misc

Navigating XML (LINQ to XML series – Part 3)

In my last two posts (part 1 and part 2) I’ve been introducing you to the the new XML classes in .NET 3.5. In this post I’ll continue that and show you some of the ways to navigate through XML.

First of all, lets start with a simple hierarchy of XML elements:

XElement root = new XElement("FirstGeneration",
                    new XElement("SecondGeneration",
                        new XElement("ThirdGeneration",
                            new XElement("FourthGeneration"))));

Which looks like this when rendered as XML:

<FirstGeneration>

<SecondGeneration>

<ThirdGeneration>

<FourthGeneration />

</ThirdGeneration>

</SecondGeneration>

</FirstGeneration>

Also in the last post I used Element to get a specific element from the current element. For example:

XElement child = root.Element("SecondGeneration");

Elements

If root (or FirstGeneration) only had one child element called “SecondGeneration” then everything is fine, you get what you asked for. However, if it contains multiple children all called “SecondGeneration” then you will only get the first element called “SecondGeneration”.

For example, if you add the following to the code above:

root.Add(new XElement("SecondGeneration","2"));
root.Add(new XElement("SecondGeneration","3"));

You will get a piece of XML that looks like this:

<FirstGeneration>

<SecondGeneration>

<ThirdGeneration>

<FourthGeneration />

</ThirdGeneration>

</SecondGeneration>

<SecondGeneration>2</SecondGeneration>

<SecondGeneration>3</SecondGeneration>

</FirstGeneration>

If you want to get all those additional children called “SecondGeneration” you will need to use the Elements (note the plural) method. For example:

IEnumerable<XElement> children = root.Elements("SecondGeneration");

You’ll also note that we don’t get a collection returned but an enumerable. This give us the opportunity to exploit many of the new extension methods. But I’ll leave them for another post. For the moment, we just need to know that it make it easy for us to enumerate over the data using a foreach loop. For example:

foreach (XElement child in children)
{
    Console.WriteLine(child);
    Console.WriteLine(new string('-', 50));
}

This will write out:

<SecondGeneration>

<ThirdGeneration>

<FourthGeneration />

</ThirdGeneration>

</SecondGeneration>

————————————————–

<SecondGeneration>2</SecondGeneration>

————————————————–

<SecondGeneration>3</SecondGeneration>

————————————————–

Parent

Using the same root object as above, we can see how to navigate back up the XML tree using Parent.

XElement grandchild = root.Element("SecondGeneration").Element("ThirdGeneration");
Console.WriteLine(grandchild.Parent);

The result of the code will be that the SecondGeneration element is printed.

 

Technorati Tags: ,,,
Misc

Fizz-Buzz in LINQ

This just occurred to me. It is somewhat pointless, but I thought it was interesting:

static void Main(string[] args)
{
    var result = from say in Enumerable.Range(1, 100)
                 select (say % 15 == 0) ? "BuzzFizz" :
                    (say % 5 == 0) ? "Buzz" :
                        (say % 3 == 0) ? "Fizz" : say.ToString();
    foreach (string say in result)
        Console.WriteLine(say);
}
Technorati Tags: ,,
Misc

Getting values out of XML in .NET 3.5 (LINQ to XML series part 2)

In my last post I gave a brief introduction to some of the new XML classes available in .NET 3.5. In this post I’ll continue that introduction by explaining how to get information out of the XML.

First off, lets assume we have some XML that looks like this:

XElement root = new XElement("root",
    new XAttribute("Attribute", "TheValue"),
    new XElement("FirstChild"),
    new XElement("SecondChild", new XElement("Grandchild", "The content of the grandchild")));

or, if you prefer in XML format, like this:

<root Attribute=”TheValue”>

<FirstChild />

<SecondChild>

<Grandchild>The content of the grandchild</Grandchild>

</SecondChild>

</root>

There are a number of ways to get the content of the grandchild element. For example:

Console.WriteLine(root.Element("SecondChild").Element("Grandchild").Value);

Value returns a string which contains the content of the element specified. In the above case it will output:

The content of the grandchild

However, you need to watch out for when there are child elements of the thing you want the value of as their content is included when you get the value. For example, if the above XML is extended so that it looks like this:

<root Attribute=”TheValue”>

<FirstChild />

<SecondChild>

<Grandchild>The content of the grandchild

<Great-grandchild>GGC content</Great-grandchild>

<Grandchild>

</SecondChild>

</root>

And the above line of C# is executed again, the result is now:

The content of the grandchildGGC content

As you can see the content of the element you want plus its child elements are now returned. This may not necessarily be what you want.

There is a second way to get the content from an element. That is to use a casting operator. You can cast the element to a number of types. In this case to a string. for example:

Console.WriteLine((string)root.Element("SecondChild").Element("Grandchild"));

The result is the same as calling Value on the element.

Be careful here, because casting an element to a string will not have the same result as calling ToString() on an element. You can see that if you simply pass the element itself to writeline (which will then call ToString() for you). For example:

Console.WriteLine(root.Element("SecondChild").Element("Grandchild"));

The result is:

<Grandchild>The content of the grandchild

<Great-grandchild>GGC content</Great-grandchild>

</Grandchild>

The process is similar if you are dealing with attribute. Using the above XML as an example, an attribute value can be retrieved like this:

Console.WriteLine(root.Attribute("Attribute").Value);

Or, using the cast operator to a string like this:

Console.WriteLine((string)root.Attribute("Attribute"));

Both of the above pieces of code output the same thing: TheValue

If you were to call WriteLine on an XAttribute object you’ll see that the ToString() method returns something slightly different. It returns this: Attribute=”TheValue”

Lets say, for instance, that the Attribute had a value of 123.456, which is a valid number representation. I mentioned earlier about casting operators on XElement and XAttribute. Well, you can cast this to a double if you prefer to get the value in that type. There is no tedious converting in your own code as the framework can handle that for you. For example:

(double)root.Attribute("Attribute")

That’s it for this post. There will be more on XML and LINQ soon.

Software Development

Introduction to LINQ to XML

Last year I wrote about the new languages features available in C# 3.0 (Anonymous Types, Extension Methods, Automatic Properties, A start on LINQ, Object Initialisers I, Object Initialisers II, & Object Initialisers III) and since then I’ve really got in to LINQ, especially LINQ to XML. The reason for that is that I hate XPath and I see LINQ to XML as a much easier way of querying XML files without faffing about with terse XPath strings. I would much rather have the ability to easily see what is going on with the query than have to figure out why my XPath isn’t working for me.

However, LINQ to XML is more than just new funky querying mechanisms. There is a whole new set of classes to deal with XML that are much easier and more intuitive than the classes that were provided back with .NET 1.0, in my opinion.

The main two classes in the new way of doing XML are XElement and XAttribute. For example, to create a new element:

XElement root = new XElement("root");

And to add an attribute to that element:

root.Add(new XAttribute(“AttributeName”, “TheValue”));

Which produces the result: <root AttributeName=”TheValue” />

If you look at the intellisense for XElement constructor you’ll see that none of the 5 overloads takes a string. The nearest is an XName. This is because there is an implicit conversion happening between a string and an XName so that creating XElements does not have to be so arduous. It would be quite irritating to have to declare XElement objects like this:

XElement root = new XElement(XName.Get("root"));

At this point you’ll find that all the VB developers will be gloating because VB9 contains a feature called XML Literals whereby the developer can just write XML directly into the source code file and VB will parse and compile it correctly. An incredibly handy feature I’m sure you’ll agree. But, since I’m a C# developer that’s what I’ll stick with – especially considering that the majority of demos of LINQ to XML I’ve seen are VB based.

If you look closely at XName’s Get method you’ll see that there are two overrides, one for an expanded name, and the other for a local name and a namespace name. The expanded name is just a string of the name with the namespace embedded in the string inside curly braces, like this:

XName.Get("{mynamespace}root");

If you prefer you can use the other overloaded version and provide two strings. The equivalent XName in that case would be created like this:

XName.Get("root", "mynamespace");

Now, you are probably wondering why a static method is being used rather than a constructor. This is because the XML classes are clever enough to reuse existing XName objects. If you create a second XName object with the same characteristics as an existing XName object it will just reuse the existing XName. For example, the following code will output “True” to the console:

XName name1 = XName.Get("{ns}MyName");
XName name2 = XName.Get("MyName", "ns");
Console.WriteLine(object.ReferenceEquals(name1, name2));

XName is immutable (it cannot change) so this is a perfectly acceptable thing to do.

The extended name notation also works if you are using strings while constructing your XElement. For example:

XElement root = new XElement("{mynamespace}root");

However, there is another way of applying namespaces in an XElement. You can use an XNamespace object and add it to the string. Like this:

XNamespace ns = XNamespace.Get("mynamespace");
XElement root = new XElement(ns + "root");

As you can probably tell the + operator has been overloaded so it can be used to add a namespace to a string to produce an XName.

Technorati Tags: ,,,,,