Overview
I've recently been using some new code to do some standard xml manipulation. Although I am pretty comfortable working with an XmlDocument I was interfacing with some code from another team and they were using the newer XDocument. I decided to give it a try and see how well it worked. Needless to say, it's very different from an XmlDocument. On the other hand, it provides a very intuitive interface and is very easy to use.
I've provided a few examples of adding and updating Elements (this APIs version of Nodes) and also of moving between the traditional XmlDocument and the newer XDocument. I used the XDocument's Parse method to create a new object directly from a string and was off to the races.
Here is the code for the examples:
Program.cs
namespace BlogConsoleDemoApp
{
class Program
{
static void Main()
{
XDocumentDemo.DemoXDocUsage();
}
}
}
XDocumentDemo.cs
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
namespace BlogConsoleDemoApp
{
public class XDocumentDemo
{
public static void DemoXDocUsage()
{
XDocument xDocSource = XDocument.Parse("<?xml version=\"1.0\" encoding=\"utf-8\"?><albums><album id=\"1\"><name>Get your Ya-Yas Out</name><artist>The Rolling Stones</artist><rating>5</rating></album><album id=\"2\"><name>The White Album</name><artist>The Beatles</artist><rating>5</rating></album><album id=\"3\"><name>Baba O'Reilly</name><artist>The Who</artist><rating>5</rating></album><album id=\"4\"><name>Houses of the Holy</name><artist>Led Zepplin</artist><rating>5</rating></album><album id=\"5\"><name>All Along the Watchtower</name><artist>Jimi Hendrix</artist><rating>5</rating></album></albums>");
XDocument xDoc = XDocument.Parse("<?xml version=\"1.0\" encoding=\"utf-8\"?><myalbums><album id=\"121\"><name>Toys in the Attic</name><artist>Aerosmith</artist><rating>4</rating></album><album id=\"34\"><name>Alive</name><artist>KISS</artist><rating>3</rating></album><album id=\"23\"><name>Goodbye Yellow Brick Road</name><artist>Elton John</artist><rating>4</rating></album><album id=\"229\"><name>Girlfriend</name><artist>Matthew Sweet</artist><rating>3</rating></album><album id=\"244\"><name>Live at the Sands</name><artist>Frank Sinatra</artist><rating>5</rating></album></myalbums>");
Trace.WriteLine("Original Xml");
Trace.WriteLine(xDoc.ToString());
// create a new rating element and replace the existing element (retrieved using XPath) with the new element
XElement oldXElement = xDoc.XPathSelectElement("myalbums/album[@id = '34']/rating");
XElement newXElement = new XElement("rating", "5");
if (oldXElement != null) oldXElement.ReplaceWith(newXElement);
Trace.WriteLine("");
Trace.WriteLine("******************************************************************************");
Trace.WriteLine("Rating changed on the KISS album");
Trace.WriteLine(xDoc.ToString());
// Get the Matthew Sweet entry using XPath and replace it with Led Zepplin
XElement oldXElementToBeReplaced = xDoc.XPathSelectElement("myalbums/album[@id = '229']");
XElement newXElementToReplaceWith = xDocSource.XPathSelectElement("albums/album[@id = '4']");
if (oldXElementToBeReplaced != null) oldXElementToBeReplaced.ReplaceWith(newXElementToReplaceWith);
Trace.WriteLine("");
Trace.WriteLine("******************************************************************************");
Trace.WriteLine("Matthew Sweet replaced by Led Zepplin");
Trace.WriteLine(xDoc.ToString());
// create and add this new element
//<album id=\"2007\"><name></name><artist>Foo Fighters</artist><rating>4</rating></album>
XElement newXElementAdd = new XElement("album");
newXElementAdd.SetAttributeValue("id", "2007");
newXElementAdd.SetElementValue("name", "There Goes My Hero");
newXElementAdd.SetElementValue("artist", "Foo Fighters");
newXElementAdd.SetElementValue("rating", "4");
if (xDoc.Root != null) xDoc.Root.Add(newXElementAdd);
Trace.WriteLine("");
Trace.WriteLine("******************************************************************************");
Trace.WriteLine("Foo Fighters Created on the fly and added");
Trace.WriteLine(xDoc.ToString());
// Convert to XmlDocument
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xDoc.ToString());
Trace.WriteLine("");
Trace.WriteLine("******************************************************************************");
Trace.WriteLine("Convert to XmlDocument for use with existing methods");
Trace.WriteLine(xmlDoc.InnerXml);
// Convert Back to XDocument
XDocument newXDoc = XDocument.Parse(xmlDoc.InnerXml);
Trace.WriteLine("");
Trace.WriteLine("******************************************************************************");
Trace.WriteLine("Convert back to XDocument when done");
Trace.WriteLine(newXDoc.ToString());
}
}
}
I hope it helps - take care until next time.
Overview:
Oftentimes developers are faced with time consuming tasks that appear to make the software being developed run slowly. The majority of code developed is sequential and synchronous. That is, step one is processed and when it is completed step two is processed. There are, however times when a process that is not critical to the current output delays the application (for instance, after retrieving a large dataset from a database for a web application and rendering some portion of it to the page we might want to cache the dataset so as not to have to make another call to the database.) Situations such as these are perfect opportunities to implement asynchronous logic and have the application be a bit faster.
In the following console application I implemented an artificial call that does nothing but wait a set amount of time before continuing with its execution. This is intended to mimic the effort required to execute some piece of useful code within our application (like the caching example in the previous paragraph). The output following the code is very clear as to the action and sequence being taken within the code.
To implement an asynchronous call one delegate is required. It must have the same signature as the method that you will be calling, in this case the DoSomeUsefulWorkDelegate. This delegate allows us to execute the method using the BeginInvoke method. This method starts the function on the ThreadPool where it gets a new thread to perform its work.
If some clean-up or exception handling needs to be performed then another delegate is also required. This second delegate is a System.AsyncCallback - the method takes one IAsyncResult parameter (in our console app we use the DoSomeUsefulWorkWrapUp method). This callback is passed into the BeginInvoke method after the original signature values (in our console app, just the secondsToSleep integer value). This method is called when the method has completed its work. The BeginInvoke also takes a final parameter that holds an object that contains state information that needs to be passed around.
The AsyncCallback method passed into the BeginInvoke will be called receiving an IAsyncResult object as its parameter. This object is the key to Asynchronous method working - it is of type System.Runtime.Remoting.Messaging.AsyncResult but it holds all of the information for the calling method, exception and other useful information in its _replyMsg [((System.Runtime.Remoting.Messaging.AsyncResult)(ar))._replyMsg]. It holds the state information passed around by the user in the AsyncState.
Console App:
using System.Diagnostics;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System;
namespace BlogConsoleDemoApp
{
class Program
{
private static int _counter = 0;
static void Main()
{
const int SLEEP_DURATION_IN_SECONDS = 2;
AsynchronousCall(SLEEP_DURATION_IN_SECONDS);
Trace.WriteLine("****");
SynchronousCall(SLEEP_DURATION_IN_SECONDS);
DoSomeUsefulWork(15);
}
private static void AsynchronousCall(int SLEEP_DURATION_IN_SECONDS)
{
PrintTimestamp("before asynchronous call to method");
DoSomeUsefulWorkDelegate delDoSomeUsefulWorkDelegate = DoSomeUsefulWorkWithAnException;
AsyncCallback ac = DoSomeUsefulWorkWrapUp;
// Call method asynchronously 6 times.
for (int i = 0; i < 6; i++)
{
delDoSomeUsefulWorkDelegate.BeginInvoke(SLEEP_DURATION_IN_SECONDS, ac, "passed around state");
}
PrintTimestamp("after asynchronous call to method");
}
private static void SynchronousCall(int secondsToSleep)
{
PrintTimestamp("before synchronous call to method");
DoSomeUsefulWork(secondsToSleep);
PrintTimestamp("after synchronous call to method");
}
private static void DoSomeUsefulWork(int secondsToSleep)
{
// sleep
Thread.Sleep(secondsToSleep*1000);
}
private static void DoSomeUsefulWorkWithAnException(int secondsToSleep)
{
if ((++_counter % 3) != 0)
{
// sleep
PrintTimestamp("before call in work with exception method");
Thread.Sleep(secondsToSleep*1000);
PrintTimestamp("after call in work with exception method");
}
else
{
int workerThreads, completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads,
out completionPortThreads);
// build a message to log
string message =
String.Format(@"Is Thread Pool Thread: {0}, Thread Id: {1} Available Worker Threads {2}",
Thread.CurrentThread.IsThreadPoolThread,
Thread.CurrentThread.GetHashCode(),
workerThreads);
throw new Exception("Exception thrown: " + message);
}
}
public delegate void DoSomeUsefulWorkDelegate(int secondsToSleep);
private static void DoSomeUsefulWorkWrapUp(IAsyncResult ar)
{
DoSomeUsefulWorkDelegate doSomeUsefulWorkDelegate = (DoSomeUsefulWorkDelegate)((AsyncResult)ar).AsyncDelegate;
try
{
doSomeUsefulWorkDelegate.EndInvoke(ar);
}
catch (Exception ex)
{
PrintTimestamp(ex.Message);
}
}
private static void PrintTimestamp(string message)
{
Trace.WriteLine(string.Format("Timestamp {0}: {1}", message, DateTime.Now.ToLongTimeString()));
}
}
}
Output:
Timestamp before asynchronous call to method: 2:02:52 AM
Timestamp before call in work with exception method: 2:02:52 AM
Timestamp after asynchronous call to method: 2:02:52 AM
****
Timestamp before synchronous call to method: 2:02:52 AM
Timestamp before call in work with exception method: 2:02:53 AM
Timestamp after synchronous call to method: 2:02:54 AM
Timestamp after call in work with exception method: 2:02:54 AM
Timestamp before call in work with exception method: 2:02:54 AM
Timestamp Exception thrown: Is Thread Pool Thread: True, Thread Id: 13 Available Worker Threads 247: 2:02:54 AM
Timestamp before call in work with exception method: 2:02:54 AM
Timestamp after call in work with exception method: 2:02:55 AM
Timestamp Exception thrown: Is Thread Pool Thread: True, Thread Id: 14 Available Worker Threads 246: 2:02:55 AM
Timestamp after call in work with exception method: 2:02:56 AM
Timestamp after call in work with exception method: 2:02:56 AM
That's it for this time - hope it helps.