http://www.developer.com/net/vb/article.php/3750661/Using-Functional-Construction-to-Create-XML-Documents.htm
I heard through the grapevine that VB.NET was mentioned on an episode of CSI: recently. Because I don't watch much TV, I missed it, but it's cool. It's a little like the invisible hand of Bill Gates reached out and said let's make VB.NET cool again. (Did VB ever stop being cool?) Or, maybe one of CSI:'s writers is a closet VB programmer. That's cool, too. Recently, you read about VB supporting literal XML in my article "Using Literal XML with Embedded Expressions in VB9." In this article, you will read about an aspect of LINQ to XML referred to as functional construction. Functional construction is a way of dynamically creating XML documents through simple method calls. The System.Xml.Linq namespace contains LINQ to XML classes such as XDocument, XElement, and XAttribute. (There are other class representing CDATA, comments, declarations, text, and processing instructions, too.) By creating instances of these classes and passing in the names and values, you can convert any data into an XML document. Constructors are essentially functions and this capability is referred to as functional construction. Once you see some code representing functional construction, you will pleased at how easy it is. First, however, you will need some data to convert to XML. To that end and to provide a complete sample solution, there are several techniques demonstrated by the sample code, including boilerplate ADO.NET code to move database data into custom entity objects. The solution is decomposed in the sub-sections that follow and the entire solution is provided as one listing at the end of the article. An entity class is a class that maps to a database table, generally. Entity classes can encompass data retrieved from a view or stored procedure too. The basic idea is that a class contains properties that map one-to-one by name and type to columns in a table. The code in Listing 1 contains an entity the AdventureWorks Customer table. (I guess even the Microsoft folks got tired of Northwind Traders, which doesn't ship with SQL Server 2005, and so there is a subtle movement to supplant it with the more advanced AdventureWorks database.) Listing 1: An entity class mapped to the AdventureWorks Customer table. Listing 1 is about as straightforward a class as possible, so go ahead and move on. The code in Listing 2 contains plain vanilla ADO.NET 2.0 that connects to the AdventureWorks database and selects all of the Customer rows. The ADO.NET code is shown in the GetCustomers function. GetCustomers returns a List(of Customer) objects (using the generic List class). GetCustomers has two helper extension methods, the factory method Create and the generic extension method SafeRead that handles checking for null fields. Listing 2: GetCustomers contains plain vanilla ADO.NET code but uses two helper extension methods for convenience. 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. 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. 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: Listing 4: The complete sample program listing. If you need a refresher on extension methods, LINQ, or any of the techniques in this article, you can check out many years of back articles on codeguru.com (in the VB Today column) or developer.com. Functional construction basically is a part of LINQ to XML that lets you construct an XML document from any data with one line of code. Because XML is self-describing and text, it is highly portable and highly desirable as a standard technology. Being able to easily move to and from an XML format is also highly desirable. Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his upcoming book LINQ Unleashed for C# due in July 2008. Paul Kimmel is an Application Architect for EDS. You may contact him for technology questions at pkimmel@softconcepts.com. Lansing is having a free Day of .NET training at Lansing Community College on June 21st. Check out the web site for details. The group likes to think of it as a poor man's TechEd (because it's free), but the content will be excellent. Copyright © 2008 by Paul T. Kimmel. All Rights Reserved.
Using Functional Construction to Create XML Documents
June 3, 2008
Introduction
Constructing an XML Document with Function Calls
Defining a Custom Entity Class
Note: In practice, the code in this part can and should be replaced with LINQ to SQL code, which follows roughly the same principles but is even easier to implement.
Public Class Customer
Private _customerID As Integer
Public Property CustomerID() As Integer
Get
Return _customerID
End Get
Set(ByVal Value AsInteger)
_customerID = Value
End Set
End Property
Private _territoryID As Integer
Public Property TerritoryID() As Integer
Get
Return _territoryID
End Get
Set(ByVal Value As Integer)
_territoryID = Value
End Set
End Property
Private _accountNumber As String
Public Property AccountNumber() As String
Get
Return _accountNumber
End Get
Set(ByVal Value As String)
_accountNumber = Value
End Set
End Property
Private _customerType As String
Public Property CustomerType() As String
Get
Return _customerType
End Get
Set(ByVal Value As String)
_customerType = Value
End Set
End Property
Private _rowGuid As Guid
Public Property RowGuid() As Guid
Get
Return _rowGuid
End Get
Set(ByVal Value As Guid)
_rowGuid = Value
End Set
End Property
Private _modifiedDate As DateTime
Public Property ModifiedDate() As DateTime
Get
Return _modifiedDate
End Get
Set(ByVal Value As DateTime)
_modifiedDate = Value
End Set
End Property
End Class
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
Constructing the 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)))
<Customers>
<Customer>
<CustomerID></CustomerID>
<TerritoryID></TerritoryID>
<AccountNumber></AccountNumber>
<rowguid></rowguid>
<ModifiedDate></ModifiedDate>
</Customer>
<Customer>
...
</Customer>
</Customers>
Imports System.Data
Imports System.Data.SqlClient
Imports System.Runtime.CompilerServices
Imports System.Text
Imports System.Reflection
Module Module1
Sub Main()
Dim customers As List(Of Customer) = GetCustomers()
'Console.WriteLine(customers.Take(5).Dump)
'Console.ReadLine()
Dim xml = ConstructXMLDocument(customers.Take(5))
Console.WriteLine(xml)
Console.ReadLine()
End Sub
Function ConstructXMLDocument(ByVal list _
As IEnumerable(Of Customer)) _
As XElement
Return 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)))
End Function
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 Dump(ByVal list As IEnumerable(Of Customer)) _
As String
Dim builder As StringBuilder = New StringBuilder()
For Each customer As Customer In list
builder.Append(customer.Dump())
builder.AppendLine()
Next
Return builder.ToString()
span style='color:blue'>End Function
<Extension()> _
Public Function Dump(ByVal customer As Customer) As String
Dim builder As StringBuilder = New StringBuilder()
Dim info() As PropertyInfo = _
customer.GetType().GetProperties()
For Each prop As Propertyinfo In info
Try
Console.WriteLine("Name: {0}, Value: {1}", _
prop.Name, _
prop.GetValue(customer, Nothing))
Catch ex As Exception
Console.WriteLine("Name: {0}, Value: {1}", _
prop.Name, "none")
End Try
Next
Return builder.ToString()
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
Public Class Customer
Private _customerID AsInteger
Public Property CustomerID() As Integer
Get
Return _customerID
End Get
Set(ByVal Value As Integer)
_customerID = Value
End Set
End Property
Private _territoryID As Integer
Public Property TerritoryID() As Integer
Get
Return _territoryID
End Get
Set(ByVal Value As Integer)
_territoryID = Value
End Set
End Property
Private _accountNumber As String
Public Property AccountNumber() As String
Get
Return _accountNumber
End Get
Set(ByVal Value As String)
_accountNumber = Value
EndSet
End Property
Private _customerType As String
Public Property CustomerType() As String
Get
Return _customerType
End Get
Set(ByVal Value As String)
_customerType = Value
End Set
End Property
Private _rowGuid As Guid
Public Property RowGuid() As Guid
Get
Return _rowGuid
End Get
Set(ByVal Value As Guid)
_rowGuid = Value
End Set
End Property
Private _modifiedDate As DateTime
Public Property ModifiedDate() As DateTime
Get
Return _modifiedDate
End Get
Set(ByVal Value As DateTime)
_modifiedDate = Value
End Set
End Property
End Class
End Module
Summary
About the Author