|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Contents
Introduction.NET 3.0 has now been released, so we should all know it by now shouldn't we. Jeez, it doesn't seem like that long ago that .NET 2.0 came along. Well for those that dont realize, .NET 3.0 actually contains quite a lot of new stuff, such as:
So as you can see there is a lot to be learned right there. I'm in the process of learning WPF/WCF but I am also interested in a little gem called LINQ, that I believe will be part of .NET 3.5 and Visual Studio "Orcas" (as its known now). LINQ will add new features to both C# and VB.NET. LINQ has three flavours:
LINQ is pretty cool, and I have been looking into it as of late, so I thought I would write an article about what I have learned in the LINQ/DLINQ/XLINQ areas, in the hopes that it may just help some of you good folk. This article will be focused on XLINQ, and is the third in a series of three articles. The article series content is as follows:
A Note About The Demo AppsBefore I start bombarding people with even more information than they can possibly handle, let me just talk briefly about the files attached to this article. This article contains two seperate zip files. The zip files are the two demo applications. DEMO application 1Is based on using Visual Studio 2005 with .NET 2.0 Framework, and the May 2006 LINQ CTP which is available here
See that the XML is syntax highlighted? This is all thanks to the free and fantastic Fireball code highlighter, available right here at Code Project. The main window allows the user to try out various XLINQ activites such as :
The 2nd form within this 1st demo application (available from "What About A Web Query" menu), uses XLINQ to get an RSS feed from Flickr, using a Flickr API string. You should not need to download anything extra for this to work, as it is all in the code. The Flickr RSS feed is then queried (this is what LINQ is all about, Language Intergrated Query Language, remember) using XLINQ, and the results are put into a new structure, using standard LINQ projection, and then the results are then used to populate a
DEMO application 2Is based on using Visual Studio 2005 with The Visual Studio Designer for WPF installed, or using Expression BLEND and Visual Studio 2005 combination, or WordPad if you prefer to write stuff in that. Obviously as it's WPF you will also need the .NET 3.0 framework, and the May 2006 LINQ CTP which is available here. The idea being that those folk that want to see how WPF/XAML will work with LINQ can have a look at this project. Don't worry to much if you dont get this code, as this application will be the subject of my next article. It's really being included for interest at this point, to show that LINQ and WPF can work quite nicely together.
This is basically doing the same thing as the 2nd form of DEMO application 1, it just looks a whole lot nicer, and showcases some cool WPF stuff like:
This application is not really that orientated to XLINQ, but it is something that I just wanted to try, so I thought, as I've done it, why not include it here. As I say if you dont get DEMO application 2, don't worry, I will be going through it in another article. It's here for interest's sake. The full article which explains this WPF application can be found here Prerequisites
Let's Get Into XLINQ ThenSo what is this XLINQ stuff? Well, it's one flavour of LINQ (Language Integrated Query) that will be part of .NET 3.5, and will certainly be part of the next verison of Visual Studio (currently called Orcas). As a recap, recall that LINQ dealt with in memory objects such as arrays and So what else can we say about this XLINQ stuff? We shall ask Microsoft what their marketing blurb is, as far as XLINQ is concerned. I asked them and they said this: "XLinq was developed with Language Integrated Query over XML in mind from the beginning. It takes advantage of the Standard Query Operators and adds query extensions specific to XML. From an XML perspective XLinq provides the query and transformation power of XQuery and XPath integrated into .NET Framework languages that implement the LINQ pattern (e.g., C#, VB, etc.). This provides a consistent query experience across LINQ enabled APIs and allows you to combine XML queries and transforms with queries from other data sources. We will go in more depth on XLinq's query capability in section 3, "Querying XML with XLinq". Just as significant as the Language Integrated Query capabilities of XLinq is the fact that XLinq represents a new, modernized in-memory XML Programming API. XLinq was designed to be a cleaner, modernized API, as well as fast and lightweight. XLinq uses modern language features (e.g., generics and nullable types) and diverges from the DOM programming model with a variety of innovations to simplify programming against XML. Even without Language Integrated Query capabilities XLinq represents a significant stride forward for XML programming. The next section of this document, "Programming XML", provides more detail on the in-memory XML Programming API aspect of XLinq." (Taken from XLINQ overview.doc, available at the LINQ Project [1] web site) I think that it is quite a good description of what XLINQ is and promises to be. Of course, it should be a good sales blurb, as XLINQ is a Microsoft invention. But what does this all mean to the average developer? This is something that this article will try and demonstrate, by the use of textual comments, and code snippets and real life working examples. Unless stated otherwise the example code shown within this article will be for discussion purposes only. But don't worry, I've gone out of my way to actually trawl the XLINQ documentation and make a nice little demo application that contains real-life working examples, of how to use XLINQ to do some of the most commonly occurring XML related tasks. So this working code is included within the DEMO application 1, and where I am specifically using the code from the DEMO application 1, I will show the following image to let you know that you can look in the code for a working example.
Obviously I have not got an example of every single thing that can be done with XLINQ, as I do have a life (not much of one, but one nonetheless). So I'll have to leave further investigation as an excercise for the reader. The way that this article is constructed is by looking at the following items, that I would consider to be very important issues that every developer that works with xml should know.
I'm hoping that by going through this set of items, that by the end, the reader will have at least a basic appreciation of what can be done with XLINQ and how it could be used inplace of using the DOM and XPath. So shall we continue? XLINQ ClassesThe overall XLINQ class heirachy is as shown below.
Taken from XLINQ overview.doc, available at the LINQ Project [1] web site. Creating New XMLIn order to undestand how XLINQ is different from existing XML document creation pratices, let's consider the following section of traditional DOM (Document Object Model) code, that creates a small XML document. XmlDocument doc = new XmlDocument();
XmlElement name = doc.CreateElement("name");
name.InnerText = "Patrick Hines";
XmlElement phone1 = doc.CreateElement("phone");
phone1.SetAttribute("type", "home");
phone1.InnerText = "206-555-0144";
XmlElement phone2 = doc.CreateElement("phone");
phone2.SetAttribute("type", "work");
phone2.InnerText = "425-555-0145";
XmlElement street1 = doc.CreateElement("street1");
street1.InnerText = "123 Main St";
XmlElement city = doc.CreateElement("city");
city.InnerText = "Mercer Island";
XmlElement state = doc.CreateElement("state");
state.InnerText = "WA";
XmlElement postal = doc.CreateElement("postal");
postal.InnerText = "68042";
XmlElement address = doc.CreateElement("address");
address.AppendChild(street1);
address.AppendChild(city);
address.AppendChild(state);
address.AppendChild(postal);
XmlElement contact = doc.CreateElement("contact");
contact.AppendChild(name);
contact.AppendChild(phone1);
contact.AppendChild(phone2);
contact.AppendChild(address);
XmlElement contacts = doc.CreateElement("contacts");
contacts.AppendChild(contact);
doc.AppendChild(contacts);
Whilst this is fairly easy to do, what is not very clear is the structure that is emmitted to the actual XML document. It is possible to work it out, for this trivial example, but if this were a large XML document, it would not be so clear. Shall we have a look at how XLINQ goes about doing the same job? XElement contacts =
new XElement("contacts",
new XElement("contact",
new XElement("name", "Patrick Hines"),
new XElement("phone", "206-555-0144",
new XAttribute("type", "home")),
new XElement("phone", "425-555-0145",
new XAttribute("type", "work")),
new XElement("address",
new XElement("street1", "123 Main St"),
new XElement("city", "Mercer Island"),
new XElement("state", "WA"),
new XElement("postal", "68042")
)
)
);
The first thing to say is that this is a lot less code, and the tree structure is almost self-evident from this listing - and it's almost as easy to read as an XML document. The other thing to note, is that in the actual syntax itself, nowhere do we see the methods
Saving XMLBoth
This option simply saves the XElements content to the location specfied by the fileName parameter.
This option simply saves the XElements content to an XmlWriter
This option simply saves the XElements content to an TextWriter
This option simply saves the XElements content to the location specfied by the fileName parameter, and preserves any white space. The same methods exist for Loading XMLBoth
This option simply loads the XML element from the location specified by the uri into a new
This option simply reads the contents of an
This option simply reads the contents of a
This option simply reads the contents of the file specfied by the fileName parameter, and preserves any white space, into a new There is also another possibility to load XML. We can parse a string by calling the XElement contacts = XElement.Parse(
@"<contacts>
<contact>
<name>Patrick Hines</name>
<phone type=""home"">206-555-0144</phone>
<phone type=""work"">425-555-0145</phone>
<address>
<street1>123 Main St</street1>
<city>Mercer Island</city>
<state>WA</state>
<postal>68042</postal>
</address>
<netWorth>10</netWorth>
</contact>
</contacts>");
Once we have loaded, or parsed a string, the full power of XLINQ may be used. We can traverse through nodes, inspect attributes, create new content, delete existing content. The same methods exist for
Traversing XMLXLINQ provides methods for getting the children of an For example if we had the following XML
And ran the following traversal foreach (XElement c in contacts.Nodes())
{
....
}
We would actually end up with the orginal XML. So how about getting to sub-elements? Well, it's very simliar syntax infact. Let's see an example shall we? foreach (XElement c in contacts.Elements("contact").Elements("address"))
{
....
}
If we are using an Do how do we deal with this, our foreach (XElement c in contactsDoc.Nodes().OfType<XElement>())
{
....
}
Working With The Root NodeSuppose that we have the following XML structure: <!--XLinq Contacts XML Example-->
<?MyApp 123-44-4444?>
<contacts numContacts="1">
<contact>
<name>sacha barber</name>
<phone type="home">01273 45426</phone>
<phone type="work">01903 205557</phone>
<address>
<street1>palmeira square</street1>
<city>brighton</city>
<county>east sussex</county>
<postcode>BN3 2FA</postcode>
</address>
</contact>
</contacts>
And we wish to work with the root node attributes. We would normally (if using DOM) need to obtain a reference to the document node, then use that to obtain the attribute vales. XLINQ does things a little differently. XElement root = contactsDoc.Root;
sb.Append(
"Examining root element [" + root.Name + "] for attributes\r\n\r\n");
if (root.HasAttributes)
{
foreach (XAttribute xa in root.Attributes())
{
sb.Append(
"Found Attibute [" + xa.Name.ToString() + "] Value=" + xa.Value);
}
}
txtResults.Document.Text = sb.ToString();
And that is enough to get the attributes of the root node. This assumes that the previous XML structure has been loaded into an
Appending New ElementsAppending new content is fairly trivial with XLINQ. It's all about getting a reference to a particular This example assumes that there is a pre-existing //obtain the root
XElement root = contactsDoc.Root;
//add new contact
root.Add(new XElement("contact",
new XElement("name", "melissa george"),
new XElement("phone", "01273 999999",
new XAttribute("type", "office")),
new XElement("phone", "01903 888888",
new XAttribute("type", "work")),
new XElement("address",
new XElement("street1", "churchill square"),
new XElement("city", "brighton"),
new XElement("county", "east sussex"),
new XElement("postcode", "BN3 4RG")
)
)
);
This one bit of code created a new contact
Updating Existing ElementsUpdating existing content is something that anyone that works with XML is going to need to do at some stage. This could consist of updating an entire elements content, or could be simply just updating one sub element for an existing element. Because I'm nice, i'll show you both. Let's assume we have the following source XML that we will update.
Update Entire ElementFor the case where we want to update an entire element with new content, as before we simply get the object we want to apply the replacement content to, and replace it using the //obtain a single contact
IEnumerable<XElement> singleContact = (from c in contactsDoc.Root.Elements(
"contact")
where ((string) c.Element(
"name")).Equals("sarah dudee")
select c);
//update contact, should only be 1
foreach (XElement xe in singleContact)
{
//use the ReplaceContent method to do the replacement
xe.ReplaceContent(new XElement("name", "sam weasel"),
new XElement("phone", "01273 111111",
new XAttribute("type", "office")),
new XElement("phone", "01903 33333",
new XAttribute("type", "work")),
new XElement("address",
new XElement("street1", "the drive"),
new XElement("city", "brighton"),
new XElement("county", "east sussex"),
new XElement("postcode", "BN3 4RG")
)
);
}
This is enough to replace the entire Contact element where where the existing Contact element that had a name of "sarah dudee" is replaced
Update Sub Element OnlyFor the case where we ONLY want to update an portion of an existing element with new content, as before we simply get the object we want to apply the replacement content to, but this time we must manipulate the elements data directly. //obtain the firstContact
IEnumerable<XElement> firstContact = ( from c in contactsDoc.Root.Elements(
"contact")
select c).Take(1);
//update contact, should only be 1
foreach (XElement xe in firstContact)
{
//UPDATE METHOD 1
xe.Element("address").SetElement("city", "MANCHESTER");
//UPDATE METHOD 2
xe.Element("address").Element("postcode").ReplaceContent("MN1");
}
This is enough to replace the the sub element of the Contact element.
Programming LINQ\Update Only 1 Contact Detail menu (this shows how to update a sub-element of an exiting element of the current Deleting Existing ElementsDeleting existing content is also going to something that anyone that works with XML is going to need to do at some stage. Let's assume we have the following source XML that we will delete from:
So as before we simply get the object we want to delete and then delete it using the //obtain the firstContact
IEnumerable<XElement> firstContact = ( from c in contactsDoc.Root.Elements(
"contact")
select c).Take(1);
//update contact, should only be 1
foreach (XElement xe in firstContact)
{
xe.Element("address").Element("county").Remove();
}
This is enough to delete the entire Contact element where the existing 1st Contact element is deleted (remember we can use any of the standard LINQ query operators, so I'm simply using Take(1) which gives us the 1st Contact element)
Querying XMLRecall in Part1 I introduced LINQ standard query operators, well guess what, XLINQ uses those very same standard query operators to allow programmers to query the XML tree in order to pull out certain parts of the tree for manipulation. Much the same as XPath allows. The only limit here, is how good your standard query operator skills are. So let's see some examples shall we? I've got four little examples for you. The following queries all work based on the same base XML, which is created within the DEMO application 1, within the Get 1st Contact QueryXElement contactsElements = createQuerySource();
XElement res = new XElement("contacts",
(from c in contactsElements.Elements("contact")
select new XElement("contact",
c.Element("name"),
new XElement("phoneNumbers", c.Elements("phone"))
)).Take(1)
);
Get All Phone Numbers QueryXElement contactsElements = createQuerySource();
foreach (XElement phone in contactsElements.Elements("contact").Elements(
"phone"))
{
//do something with the phone XElement
}
Get Specific Customer QueryXElement contactsElements = createQuerySource();
....
....
//look for this Element with the correct name requested
IEnumerable<XElement> cont = (from c in contactsElements.Elements("contact")
where (string) c.Element("name") == userName
select c);
....
....
Get ONLY Customers That Have Home Phone Numbers QueryXElement contactsElements = createQuerySource();
....
....
//look for this Element with the correct name requested
IEnumerable<XElement> res = (from c in contactsElements.Elements("contact")
where (string) c.Element(
"phone").Attribute("type") == "home"
select c);
....
....
Using XLINQ To Access 3rd Party XML DataSo how about we get XLINQ to grab some data out of an RSS Feed to show some images? After all, it should be able to cope with any XML data, even if it's not local. I decided to use Flickr as the data source, as I know that it has an API which may be used to obtain RSS Feeds with images URLs. Images are quite pretty, and are also suitable things to display in a Windows Form application. This concept is based on something that I saw in Omar Al Zabirs fantastic article which is available here So shall we have a look at the code that does this? The class diagram is as follows:
As can be seen there is a form that has some That's really all there is to it. Now some code. To be honest it's very straightforward. FlickrRSSGrabberusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Query;
using System.Xml.XLinq;
using System.Data.DLinq;
namespace Linq_3
{
public partial class FlickrRSSGrabber : Form
{
private RSSImageFeed RSSImages = new RSSImageFeed();
public FlickrRSSGrabber()
{
InitializeComponent();
}
private void btnGo_Click(object sender, EventArgs e)
{
RSSImages.PageIndex = 0;
doFlickrSearch();
}
private void doFlickrSearch()
{
string searchType = cmbSearchType.SelectedItem.ToString();
string searchWord = searchType.Equals(
"By Search Word") ? this.txtSearchWord.Text : "";
if (searchType.Equals("By Search Word") && string.IsNullOrEmpty(
searchWord))
{
MessageBox.Show(
"You MUST enter a search word, to search by keyword");
}
else
{
getFlickrData(searchType, searchWord);
}
}
private void getFlickrData(string searchType, string searchWord)
{
IEnumerable<PhotoInfo> photos = (
IEnumerable<PhotoInfo>)RSSImages.LoadPictures(
searchType, searchWord);
if (photos.Count() > 0)
{
lblPageIngex.Visible = true;
int pIndex = RSSImages.PageIndex;
lblPageIngex.Text = "Page " + (++pIndex);
//show the images, clearing old ones 1st
pnlFlowPhotos.Controls.Clear();
foreach (PhotoInfo pi in photos)
{
PictureBox pb = new PictureBox();
pb.ImageLocation = @pi.PhotoUrl(true);
pb.SizeMode = PictureBoxSizeMode.AutoSize;
toolTip1.SetToolTip(pb, pi.Title);
pnlFlowPhotos.Controls.Add(pb);
}
setNextPrevStates(RSSImages.IsPrevAvail,
RSSImages.IsNextAvail);
}
else
{
lblPageIngex.Visible = false;
setNextPrevStates(false, false);
}
}
private void setNextPrevStates(bool prevEnabled, bool nextEnabled)
{
btnPrev.Enabled = prevEnabled;
string prevTT = btnPrev.Enabled ? "Click to go back a page" :
"There are no more pages";
toolTip1.SetToolTip(btnPrev, prevTT);
btnNext.Enabled = nextEnabled;
string nextTT = btnNext.Enabled ? "Click to go forward a page" :
"There are no more pages";
toolTip1.SetToolTip(btnNext, nextTT);
}
private void cmbSearchType_SelectedValueChanged(object sender,
EventArgs e)
{
string searchType = cmbSearchType.SelectedItem.ToString();
txtSearchWord.Visible = searchType.Equals(
"By Search Word") ? true : false;
lblSearchWord.Visible = txtSearchWord.Visible;
}
private void btnNext_Click(object sender, EventArgs e)
{
RSSImages.PageIndex++;
doFlickrSearch();
}
private void btnPrev_Click(object sender, EventArgs e)
{
RSSImages.PageIndex--;
doFlickrSearch();
}
private void FlickrRSSGrabber_Shown(object sender, EventArgs e)
{
cmbSearchType.SelectedItem = "Most Recent";
//getData from Flickr
getFlickrData("MOST_RECENT", "");
}
}
}
RSSImageFeedTo understand how this class uses XLINQ to obtain the Flickr data, one first needs to understand what the raw XML data is, that Flickr provides. It can be seen that there are three possible URLs listed below, for :
One of these three different URLs is triggered depending on the value that the user picks from the combobox (cmbSearchType) within the So let's examine a small subset of what Flickr gives us for one of these requests. Let's take the MOST_RECENT URL http://www.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=c705bfbf75e8d40f584c8a946cf0834c We end up with something like this. Try it yourself in FireFox, or your favourite browser. <rsp stat="ok">
<photos page="1" pages="10" perpage="100" total="1000">
<photo id="493415381" owner="8201860@N03" secret="dba0cae590" server="225"
farm="1" title="setting up 6" ispublic="1" isfriend="0" isfamily="0"/>
<photo id="493415375" owner="42802631@N00" secret="596a13de8e" server="226"
farm="1" title="DSC01713" ispublic="1" isfriend="0" isfamily="0"/>
<photo id="493392504" owner="59616645@N00" secret="49459bb023" server="191"
farm="1" title="IMG_0841" ispublic="1" isfriend="0" isfamily="0"/>
<photo id="493392488" owner="28532182@N00" secret="ebcdc8d2d8" server="220"
farm="1" title="img_1438" ispublic="1" isfriend="0" isfamily="0"/>
Looking at this structure, it's fairly easy to see how this small bit of XLINQ syntax (taken from the full code listing shown below) is working. //select the RSS data from Flickr, and use standard LINQ projection
//to store it within a new PhotoInfo which is an object of my own making
IEnumerable<PhotoInfo> photos = (from photo in xroot.Element(
"photos").Elements("photo")
select new PhotoInfo
{
Id = (string)photo.Attribute("id"),
Owner = (string)photo.Attribute("owner"),
Title = (string)photo.Attribute("title"),
Secret = (string)photo.Attribute("secret"),
Server = (string)photo.Attribute("server"),
Farm = (string)photo.Attribute("Farm"),
}).Skip(pageIndex * columns * rows).Take(columns * rows);
There is almost a direct mapping here, and it's very easy to read. Compare this with the iterative process that one would have to do if using the DOM, or compare it to its equivalent XPath. Me personally, I prefer XPath over the iterative DOM process any day of the week, but I prefer XLINQ process to XPath process. It's just so easy to read. Anyway, full code listing for those that want the whole picture. using System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using System.Xml.XLinq;
using System.Data.DLinq;
namespace Linq_3
{
public class RSSImageFeed
{
private const string FLICKR_API_KEY =
"c705bfbf75e8d40f584c8a946cf0834c";
private const string MOST_RECENT =
"http://www.flickr.com/services/rest/?method=flickr.photos.">http://www.flickr.com/services/rest/?method=flickr.photos. +
"getRecent&api_key=" + FLICKR_API_KEY;
private const string INTERESTING =
"http://www.flickr.com/services/rest/?method=flickr.">http://www.flickr.com/services/rest/?method=flickr. +
"interestingness.getList&api_key=" + FLICKR_API_KEY;
private const string ENTER_TAG = "http://www.flickr.com/services">http://www.flickr.com/services +
"/rest/?method=flickr.photos.search&api_key=" +
FLICKR_API_KEY + "&tags=";
private string url = MOST_RECENT;
private int pageIndex = 0;
private int columns = 5;
private int rows = 2;
private bool prevAvail = false;
private bool nextAvail = false;
public RSSImageFeed()
{
}
public bool IsPrevAvail
{
get { return prevAvail; }
}
public bool IsNextAvail
{
get { return nextAvail; }
}
public int PageIndex
{
set { pageIndex = value; }
get { return pageIndex; }
}
public IEnumerable<PhotoInfo> LoadPictures(string searchType,
string searchWord)
{
switch (searchType)
{
case "Most Recent":
this.url = MOST_RECENT;
break;
case "Interesting":
this.url = INTERESTING;
break;
case "By Search Word":
this.url = ENTER_TAG + searchWord;
break;
default:
this.url = MOST_RECENT;
break;
}
try
{
var xraw = XElement.Load(url);
var xroot = XElement.Parse(xraw.Xml);
//select the RSS data from Flickr, and use standard LINQ
//projection to store it within a new PhotoInfo which is
//an object of my own making
IEnumerable<PhotoInfo> photos = (
from photo in xroot.Element("photos").Elements("photo")
select new PhotoInfo
{
Id = (string)photo.Attribute("id"),
Owner = (string)photo.Attribute("owner"),
Title = (string)photo.Attribute("title"),
Secret = (string)photo.Attribute("secret"),
Server = (string)photo.Attribute("server"),
Farm = (string)photo.Attribute("Farm"),
}).Skip(pageIndex * columns * rows).Take(columns * rows);
//set the allowable next/prev states
int count = photos.Count();
if (pageIndex == 0)
{
this.prevAvail = false;
this.nextAvail = true;
}
else
{
this.prevAvail = true;
}
//see if there are less photos than sum(Columns * Rows)
//if there are less cant allow next operation
if (count < columns * rows)
{
this.nextAvail = false;
}
return photos;
}
catch (Exception ex)
{
return null;
}
}
}
}
PhotoInfousing System;
using System.Collections.Generic;
using System.Text;
namespace Linq_3
{
public class PhotoInfo
{
private const string FLICKR_SERVER_URL = "http://static.flickr.com/";
private const string FLICKR_PHOTO_URL =
"http://www.flickr.com/photos/";
public string Id;
public string Owner;
public string Title;
public string Secret;
public string Server;
public string Farm;
public bool IsPublic;
public bool IsFriend;
public bool IsFamily;
public string PhotoUrl(bool small)
{
return FLICKR_SERVER_URL + this.Server + '/' +
this.Id + '_' + this.Secret + (small ? "_s.jpg" : "_m.jpg");
}
public string PhotoPageUrl
{
get { return FLICKR_PHOTO_URL + this.Owner + '/' + this.Id; }
}
}
}
So putting these three classes together we are able to come up with a little Flickr viewer that allows users to search Most Recent/Interesting or by Key Word, and allows the user to page through the results, if there is more than 1 page worth of photos. Nice huh?
As originally stated I have also included a WPF version of this XLINQ experiment, simply beacuse I am also learning WPF, and I think it looks cool. Some folks may also like to see how WPF can work with XML bound data, as is the case for this WPF application. Like I said earlier, if you don't get the WPF version, don't worry, that will be the focus of another article.
Thats itI hope there are some of you that have read Part1 and Part2 of this article series, and can see the similarities between all the different flavours of LINQ. I am also hoping that this article has shown that XLINQ (Or LINQ over XML as it will be known in future) is not that scary, and its actually quite easy to use, even when using RSS and 3rd party XML data sources. As a closing note on this series, I just want to tell people, I didn't have much knowledge about LINQ when I started, I just got my head down and went for it. Some of it is a little frustrating, but to be honest I managed to get all the demo apps doing what I wanted (not the WPF one, that is a different story) inside a single day, which is fairly good for a new technology I think. So say (or think) what you will, about LINQ, I think it is set to radically change the ways in which we work with data at every level. It may be that you will choose to only use LINQ, or only XLINQ or all that LINQ has to offer. I know that I will probably be trying out all three at some stage on a live system. Happy LINQing. So What Do You Think ?I would just like to ask, if you liked the article please vote for it, and leave some comments, as it allows me to know if the article was at the right level or not, and whether it contained what people need to know. ConclusionI have quite enjoyed constructing this article, and have been quite refreshed at just how easy XLINQ is to actually use. In fact I wish I had of done this XLINQ article 1st, as I recently finished a short term contract, where we were doing lots of XML parsing which would have been far easier with XLINQ. I also think the way in which XML data is updated is far superior in XLINQ to conventional methods, it just seems more logical the way its done in XLINQ. No more iterative processes, hooray. Historyv1.0 10/05/07: Initial issue Bibilography
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||