Strong typed XLinq (experiment 1)
If your are unfamiliar with LINQ and XLINQ, read this first.
When you look at how DLinq was implemented, they are using a collection of classes to describe the structure of the database in such a way that database artifacts such as Tables and Columns become first class citizens in .NET.
However, this has not been done for XLinq currently. So I decided to experiment a bit to see if this was doable for XLinq also. Having ‘strong typed’ XLinq support can be thought of on a number of levels:
- Being able to use element names and attribute names directly
- Have the compiler ‘validate’ the schema, so that when you are using constructs that the schema does not allow, you get compiler warnings
- Have the above features with the same performance as without the ‘extra’ layer
My first experiment is only about doing the first item on that list. It turns out to be as simple as I expected… Normally, using XLinq, your code would look something like this:
XElement contacts =
new XElement(“contacts”,
new XElement(“contact”,
new XElement(“name”, “Patrick Hines”),
new XElement(“phone”, “206-555-0144”),
new XElement(“address”,
new XElement(“street1”, “
new XElement(“city”, “
new XElement(“state”, “WA”),
new XElement(“postal”, “68042”)
)
)
);
As you can see, this involves a lot of strings to give the elements a name. I wanted to fix this so that instead of XElement(”Foo”), you can just use Foo as a class.
The following sample code demonstrates this.
using System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using System.Xml.XLinq;
using System.Data.DLinq;
using ContactsSchema;
namespace XLinqSchema
{
class Program
{
static void Main(string[] args)
{
int[] numbers = new int[]{1,2,3,4,5,6,7,8,9,10};
string[] names = new string[]{“Arie”,“Bernard”,“Cornelis”,“David”,“Eduard”,
“Frits”,“Gerard”,“Hendrik”,“Isaac”,“Johannes”};
var expr = new Contacts(
from i in numbers
select new Contact(
new Id(i),
new Name(names[i-1]))
);
Console.WriteLine(expr);
Console.ReadLine();
}
}
}
When this code is run, it outputs the following:
<Contacts >
<Contact Id=”1″>
<Name>Arie</Name>
</Contact>
<Contact Id=”2″>
<Name>Bernard</Name>
</Contact>
<Contact Id=”3″>
<Name>Cornelis</Name>
</Contact>
<Contact Id=”4″>
<Name>David</Name>
</Contact>
<Contact Id=”5″>
<Name>Eduard</Name>
</Contact>
<Contact Id=”6″>
<Name>Frits</Name>
</Contact>
<Contact Id=”7″>
<Name>Gerard</Name>
</Contact>
<Contact Id=”8″>
<Name>Hendrik</Name>
</Contact>
<Contact Id=”9″>
<Name>Isaac</Name>
</Contact>
<Contact Id=”10″>
<Name>Johannes</Name>
</Contact>
</Contacts>
Making this happen was not really hard to do. I inherited from XElement with the strong typed counterpart, implementing all constructors, with the difference that the XName does not have to be filled anymore.
See the code below:
using System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using System.Xml.XLinq;
using System.Collections;
namespace ContactsSchema
{
public class Constants
{
public const string TargetNamespace = “urn:mycontacts”;
}
public class Contacts : XElement
{
public static readonly XName ElementName = XName.Get(“Contacts”, Constants.TargetNamespace);
public Contacts(Contacts element)
: base(element)
{
}
public Contacts(params object[] childItems)
: base(ElementName, childItems)
{
}
///
/// Constructs an empty list
///
public Contacts()
: base(ElementName)
{
}
}
public class Contact : XElement
{
public static readonly XName ElementName = XName.Get(“Contact”, Constants.TargetNamespace);
public Contact(Contact contactElement)
: base(contactElement)
{
}
public Contact(params object[] childItems)
: base(ElementName, childItems)
{
}
public Contact()
: base(ElementName)
{
}
}
public class Name : XElement
{
public static readonly XName ElementName = XName.Get(“Name”, Constants.TargetNamespace);
public Name(Name contactElement)
: base(contactElement)
{
}
// No elements allowed below this
public Name(object value)
: base(ElementName, value)
{
}
public Name()
: base(ElementName)
{
}
}
public class Id : XAttribute
{
public Id(Id attribute)
: base(attribute)
{
}
public Id(object value)
: base(“Id”, value)
{
}
}
}
In a following experiment, I want to try to make the needed ‘Mapping code’ even more compact, and also support more Schema aware features, such as performing a validation of the object tree, or if possible, make the compiler complain about schema violations…
Until then, happy Linqing..