Using Functional Construction to Create XML Documents, Page 2
Listing 2: GetCustomers contains plain vanilla ADO.NET code but uses two helper extension methods for convenience.
Private Function GetCustomers() As List(Of Customer) Dim connectionString As String = _ "Data Source=.\SQLExpress;Initial Catalog=AdventureWorks;" + _ "Integrated Security=True" Dim customers As List(Of Customer) = New List(Of Customer) Using connection As SqlConnection = _ New SqlConnection(connectionString) connection.Open() Dim command As SqlCommand = New SqlCommand( _ "SELECT * FROM Sales.Customer", connection) Dim reader As SqlDataReader = command.ExecuteReader While (reader.Read()) customers.Add(New Customer().Create(reader)) End While End Using Return customers End Function <Extension()> _ Public Function Create(ByVal customer As Customer, _ ByVal reader _ As SqlDataReader) As Customer customer.AccountNumber = customer.SafeRead(reader, _ "AccountNumber", "") customer.CustomerID = customer.SafeRead(reader, _ "CustomerID", -1) customer.TerritoryID = customer.SafeRead(reader, _ "TerritoryID", -1) customer.CustomerType = customer.SafeRead(reader, _ "CustomerType", "S") customer.RowGuid = customer.SafeRead(reader, "rowguid", _ Guid.NewGuid()) customer.ModifiedDate = customer.SafeRead(reader, _ "ModifiedDate", DateTime.Now) Return customer End Function <Extension()> _ Function SafeRead(Of T)(ByVal customer As Customer, _ ByVal reader As SqlDataReader, ByVal fieldName As String, _ ByVal defaultValue As T) As T Try Return Convert.ChangeType(reader(fieldName), _ defaultValue.GetType()) Catch ex As Exception Return defaultValue End Try End Function
Create is an implementation of the factory pattern. A factory pattern is used to construct objects that have slightly complicated construction needs. In this case, the factory pattern was used because you need to get the Customer fields from the SqlDataReader. SafeReader is used to handle nulls. (In practice, add a check to see whether reader(fieldname) returns Nothing; this will prevent a few extra unnecessary exceptions.)
Extension methods are an implementation of the decorator pattern. By using the ExtensionAttribute on methods, you can avoid inheritance where it is inconvenient or impossible—as is the case with classes marked NotInheritable (or sealed in C#). Extension method permits you to program with member semantics against methods that are not literally members. The ExtensionAttribute marks a method as an extension method, and the first argument indicates the type being extended. For example, SafeRead has a first argument of Customer. This means you can call SafeRead as if it were a member of the Customer class.
Constructing the XML Document
The code you have seen so far all works toward getting a list of Customer objects. Now that you have that data, you easily can convert the List(Of Customer) objects into an XML document. The fragment in Listing does this for you.
Listing 3: Using LINQ and functional construction to convert a List(Of Customer) objects to an XML document.
New XElement("Customers", _ From customer In list _ Select New XElement("Customer", _ New XElement("CustomerID", customer.CustomerID), _ New XElement("TerritoryID", customer.TerritoryID), _ New XElement("AccountNumber", customer.AccountNumber), _ New XElement("rowguid", customer.RowGuid), _ New XElement("ModifiedDate", customer.ModifiedDate)))
The first part of the fragment creates a new XElement object. Everything else is simply additional parameters supplied to that XElement constructor. (The XElement constructor supports an optional and arbitrary number of inputs through the ParamArray modifier.)
The additional arguments are supplied by the embedded LINQ query. The From customer In list defines a range value customer against a supplied list. (The list value will be the List(Of Customer) objects. The Select clause uses projection and functional construction to create subordinate XElement arguments for each of the Customer rows and columns. The result is an XML document that looks like this:
<Customers> <Customer> <CustomerID></CustomerID> <TerritoryID></TerritoryID> <AccountNumber></AccountNumber> <rowguid></rowguid> <ModifiedDate></ModifiedDate> </Customer> <Customer> ... </Customer> </Customers>