XLINQ Introduction Part 3 Of 3(转与CodeProject)

来源:百度文库 编辑:神马文学网 时间:2024/06/12 21:59:39
XLINQ Introduction Part 3 Of 3
BySacha Barber.
An introduction to LINQ
C# (C# 3.0)
Windows (Vista), .NET (.NET 3.0)
Win32, VS (VS2005)
Dev
Posted: 11 May 2007
Views: 368
Note:This is an unedited reader contributionModify |Delete
Announcements
Vista API competition$10,000 in prizes
Vista Mobile comp:Win a Samsung UMPC
Vista Secure APIcomp: Win a Laptop!
Monthly Competition
Search   Articles Authors   Advanced Search
Sitemap
PrintBroken Article?BookmarkDiscussSend to a friend
12 votes for this article.
Popularity: 5.17. Rating: 4.79 out of 5.
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else‘s work without reference then pleaseReport this article.
Download demo application1 for .NET 2.0 (includes src code) -2,133 Kb Download demo application2 for .NET 3.0 / WPF (includes src code) - 2,618 Kb
Contents
IntroductionA Note About The Demo AppsPrerequisitesLets Get Into XLINQ ThenXLINQ ClassesCreating New XmlSaving XmlLoading XmlTraversing XmlWorking With The Root NodeAppending New ElementsUpdating Existing ElementsDeleting Existing ElementsQuerying XmlUsing XLINQ To Access 3rd Party Xml Data
Thats itConclusionHistoryBibilography
.NET 3.0 has now been released, so we should all know it by now shouldn‘t we. Jeez, 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 :
Windows Workflow Foundation (WWF) : Managing object lifecycles / persistent object storage Windows Communication Foundation (WCF) : The new communication layer Windows Presentation Foundation (WPF) : The new presenstation layer (XAML) Windows Cardspace : Which provides a standards-based solution for working with and managing diverse digital identities
So as you can see there is a lot to be learnt 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 Stuio "Orcas" (as its known now). LINQ will add new features to both C# and VB .NET. LINQ has 3 flavours
LINQ : Language Integrated Query for in memory objects (renamed in March 2007 Orcas CTP to LINQ to objects) DINQ : Language Integrated Query for databases (renamed in March 2007 Orcas CTP to LINQ to SQL) XLINQ : Language Integrated Query for XML (renamed in March 2007 Orcas CTP to LINQ to XML)
LINQ is pretty cool, and I have been looking into it of late, so I thought I would write an article about what I have learnt in the LINQ/DLINQ/XLINQ areas, in the hope that it may just help some of you good folk. This article will be focussed on XLINQ, and is the third in a series of 3 proposed articles.
The proposed article series content will be as follows:
Part1 : will be all about standard LINQ, which is used to query in memory data objects such as List, arrays etc etcPart2 : will be about using DLINQ, which is LINQ for database data Part3 (this article) : will be about using XLINQ, which is LINQ for XML data
Before 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 2 seperate zip files. The zip files are the 2 demo applications.
DEMO application 1
Is based on using Visual Studio 2005 with .NET 2.0 framework, and the May 2006 LINQ CTP which is availablehere

It can be seen that the XML is syntax highlighted, this is all thanks to the free and fantastic Fireball code highlighter, available righthere at codeproject.
The main window allows the user to try out various XLINQ activites such as :
Create a new XML tree section Create a new XML document Open an existing XML document Find root node attributes Query the XML using standard LINQ query operators as described inPart1 of this series Updating XML sections Deleting XML sections
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 FlowLayoutPanel with PictureBox controls that represent the gathered images. The user is also free to change the type of Flickr search being performed, and may page through the results obtained.

DEMO application 2
Is based on using Visual Studio 2005 withThe Visual Studio Designer for WPF installed, or usingExpression BLEND and Visual Studio 2005 combination, or wordpad if you prefer to write stuff in that.
Obviously as its WPF you will also need the.NET 3.0 framework, and the May 2006 LINQ CTP which is availablehere. The idea being that those folk that want to see how WPF/XAML will work with LINQ can have a look at this project. Dont worry to much if you dont get this code, as this application will be the subject of my next article, its really being included for interest at this point, to show that LINQ and WPF can work quite nicely together actually



This is basically doing the same thing as the 2nd form of DEMO application 1, it just looks a whole lot nicer, and show cases some cool WPF stuff like:
Animations Data binding Templating Styles Resources XLINQ usage, of course
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. Its here for interest sake really.
To run the code supplied with this article you will need to install the May 2006 LINQ CTP which is availablehere, there is a new March 2007 CTP available, but its about 4GB for the full install (as its not just LINQ but the entire next generation of Visual Studio codenamed "Orcas") and quite fiddly, and probably going to change anyway, so the May 2006 LINQ CTP will be OK for the purpose of what this article is trying to demonstrate. DEMO application 1, makes use of the excellent Fireball code highlighter, available righthere at codeproject. DEMO application 2, makes use of the .NET 3.0 framework which is available for downloadhere
So what is this XLINQ stuff, well its 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 List and Dictionary objects, whilst DLINQ or LINQ over SQL dealt with entities and database interaction. So any guesses what XLINQ is all about, well its actually XML over LINQ.
So what else can we say about this XLINQ stuff, shall we ask Microsoft what their marketing blurb is, as far as XLINQ is concerned. Well 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 actually 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, and it is their words after all. 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 dont 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.
When you see this image, this means that I have created a worked example for you in the DEMO application 1
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 all the same. 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.
The XLINQ class structure Creating new xml Saving xml Loading xml Traversing xml Working with the root node Appending new elements Updating existing elements Deleting existing elements Querying xml Using XLINQ to access 3rd party xml data
I‘m hoping that by going through this set of items, 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.
The overall XLINQ class heirachy is as shown below.

• Although XElement is low in the class hierarchy, it is the fundamental class in XLinq. XML trees are generally made up of a tree of XElements. XAttributes are name/value pairs associated with an XElement. XDocuments are created only if necessary, such as to hold a DTD or top level XML processing instruction (XProcessingInstruction). All other XNodes can only be leaf nodes under an XElement, or possibly an XDocument (if they exist at the root level).
• XAttribute and XNode are peers and not derived from a common base class (other than object). This reflects the fact that XML attributes are really name value pairs associated with an XML element not nodes in the XML tree. Contrast this with W3C DOM.
• XText is exposed in this version of XLinq, but as discussed above, it is best to think of it as a semi-hidden implementation detail except when exposing text nodes is necessary. As a user, you can get back the value of the text within an element or attribute as a string or other simple value.
• The only XNode that can have children is an XContainer, meaning either an XDocument or XElement. An XDocument can contain an XElement (the root element), an XDeclaration, an XDocumentType, or an XProcessingInstruction. An XElement can contain another XElement, an XComment, an XProcessingInstruction, and text (which can be passed in a variety of formats, but will be represented in the XML tree as text).
Taken from XLINQ overview.doc, available at the LINQ Project [1] web site
In order to undestand how XLINQ is different from existing XML document creation pratices, lets consider the following section of traditional DOM (Document Object Model) code, that creates a small xml document
Collapse
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. Ok 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 it is a lot less code, and the tree structure is almost self evident from this listing, its almost as easy to read as an xml document would be. The other thing to note, is the actual syntax itself, no where do we see the methods createElement(), or AppendChild(). Instead we see new XElement which sort of makes more sense, at least in how it actually reads. Personally, I think someone who knows xml, but has not used DOM before would probably understand this new syntax a bit better, as it seems to be a closer match in term of the actual terminology used. Xml man knows of an new element as an new element, not as AppendChild(). I guess in the end, this is really down to preference.
DEMO application 1, contains 3 menus to further demonstrate this concept, look at :
Programming LINQ\Create Elements menu Programming LINQ\Create Entire Document menu Programming LINQ\Create Elements With Namespaces menu
Both XDocument and XElement expose an overloaded Save() method, the options are as follows:
XDocument.Save(string fileName)
This option simply saves the XElements content to the location specfied by the fileName parameter
XDocument.Save(System.Xml.XmlWriter writer)
This option simply saves the XElements content to an XmlWriter
XDocument.Save(TextWriter textWriter)
This option simply saves the XElements content to an TextWriter
XDocument.Save(string fileName, bool preserveWhitespace)
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 XElement
Both XDocument and XElement exposes an overloaded Load() method, the options are as follows:
XDocument.Load(string uri)
This option simply loads the xml element from the location specified by the uri into a new XDocument
XDocument.Save(System.Xml.XmlReader reader)
This option simply reads the contents of an XmlReader into a new XDocument
XDocument.Save(TextReader textReader)
This option simply reads the contents of an TextReader into a new XDocument
XDocument.Save(uri fileName, bool preserveWhitespace)
This option simply read the contents of the file specfied by the fileName parameter, and preserves any white space, into a new XDocument
There is also another possibility to load xml. We can parse a string by calling the XDocument.Parse method, so lets see an example of that shall we.
XElement contacts = XElement.Parse( @" Patrick Hines 206-555-0144 425-555-0145
123 Main St Mercer Island WA 68042
10
"); One 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 XElement
DEMO application 1, contains 2 areas to further demonstrate this concept, look at :
Programming LINQ\Opem Existing File menu (this shows the Load() method) Look at the createQuerySource() method (this shows the Parse() method)
XLinq provides methods for getting the children of an XElement. To get all of the children of an XElement (or XDocument), you can use the Nodes() method.
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 its very simliar syntax infact. Lets see an example shall we.
foreach (XElement c in contacts.Elements("contact").Elements("address")) { .... }
What if we are using a XDocument, this returns IEnumerable because you could have text mixed with other XLinq types, such as XDeclaration, XComment and XProcessingInstruction as well as XElement
Do how do we deal with this, our foreach (XElement in .... is going to fail for non XElement cases. What do we do about that, well quite simply put we do the following
foreach (XElement c in contactsDoc.Nodes().OfType()) { .... }
DEMO application 1, contains 3 menus to further demonstrate this concept, look at :
Traversing\Show All menu (this shows all Elements) Traversing\Show only Elements menu (this shows ONLY XElement types) Traversing\Show Sub Elements menu (this shows the address sub elements of a Contact element)
Suppose that we have the following XML structure
sacha barber 01273 45426 01903 205557
palmeira square brighton east sussex BN3 2FA

And that 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 XDocument called contactsDoc. The result can be seen from this screen shot from DEMO application 1

DEMO application 1, contains 1 menu to further demonstrate this concept, look at :
Programming LINQ\Get Root Node Attribute Values menu (this shows how to get the root node attributes)
Appening new content is fairly trivial with XLINQ. Its all about getting a reference to a particular XDocument or XElement that you wish to add new content to. After that the work is fairly easy. Lets see an example shall we.
This example assumes that there is a pre-existing XDocument called contactsDoc.
//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 XElement which is then appended to the root element of the existing XDocument called contactsDoc. Thats all you have to do, Get the object you want to add content to, and add the content using the Add() method
DEMO application 1, contains 1 menu to further demonstrate this concept, look at :
Programming LINQ\Append New Contact menu (this shows how to add a new Contact to the root element of the current XDocument)
Updating existing content is going to 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 1 sub element for an existing element. Because i‘m nice, i‘ll show you both.
Lets assume we have the following source XML that we will update

Update Entire Element
For 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 ReplaceContent() method.
Collapse
//obtain a single contact IEnumerable 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
DEMO application 1, contains 1 menu to further demonstrate this concept, look at :
Programming LINQ\Update Entire Contact Element menu (this shows how to update an entire Contact element of the current XDocument)
Update Sub Element Only
For 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 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
DEMO application 1, contains 1 menu to further demonstrate this concept, look at :
Programming LINQ\Update Only 1 Contact Detail menu (this shows how to update a sub-element of an exiting element of the current XDocument)
Deleting existing content is also going to something that anyone that works with XML is going to need to do at some stage.
Lets 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 Remove() method
//obtain the firstContact IEnumerable 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 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)
DEMO application 1, contains 1 menu to further demonstrate this concept, look at :
Programming LINQ\Delete A Contact menu (this shows how to delete an entire Contact element out of the current XDocument)
Recall inPart1 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 lets see some examples shall we. Ive got 4 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 createQuerySource() method.
Get 1st Contact Query
XElement 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 Query
XElement contactsElements = createQuerySource(); foreach (XElement phone in contactsElements.Elements("contact").Elements("phone")) { //do something with the phone XElement } Get Specific Customer Query
XElement contactsElements = createQuerySource(); .... .... //look for this Element with the correct name requested IEnumerable cont = (from c in contactsElements.Elements("contact") where (string) c.Element("name") == userName select c); .... .... Get ONLY Customers That Have Home Phone Numbers Query
XElement contactsElements = createQuerySource(); .... .... //look for this Element with the correct name requested IEnumerable res = (from c in contactsElements.Elements("contact") where (string) c.Element("phone").Attribute("type") == "home" select c); .... ....
DEMO application 1, contains 4 menus to further demonstrate this concept, look at :
Queries\Get 1st Contact menu (Get the 1st Contact element) Queries\Get Phone Numbers menu (Get the phone numbers of all Contact elements) Queries\Get Specific Customer menu (Prompts the user for a Contact name and gets the Contact with that name, if it exists) Queries\Get Customers That Have Home phone numbers menu (Gets all Contacts that have home phone number attributes)
So 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 its 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. Image are quite pretty, and are also suitable things to display in a windows forms application. This concept is based on something that I saw in Omar Al Zabirs fantastic article which is availablehere
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 Button (btnGo/btnPrev/btnNext) on it and a ComboBox (cmbSearchType) which are used to create the correct parametized RSS feed request from Flickr. The Flickr request is made by the RSSImageFeed class, which returns a IEnumerable object which represents the photos found by the current RSS Feed request.
Thats really all there is to it. So now some code, to be honest its very straight forward
FlickrRSSGrabber
Collapse
using 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 photos = (IEnumerable)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", ""); } } }
RSSImageFeed
To 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 3 possible URLs listed below, for :
MOST_RECENT INTERESTING ENTER_TAG
One of these 3 different URLs is triggered dependant on to the value that the user picks from the combobox (cmbSearchType) within the FlickrRSSGrabber class shown above.
So lets examine a small subset of what Flickr gives us for one of these requests, lets 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

Looking at this structure, its 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 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 amost a direct mapping here, and its 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. Its just so easy to read.
Anyway full code listing for those that want the whole picture.
Collapse
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.getRecent&api_key=" + FLICKR_API_KEY; private const string INTERESTING = "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/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 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 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; } } } }
PhotoInfo
Collapse
using 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 3 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, is 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 folk may also like to see how WPF can work with XML bound data, as is the case for this WPF application. Like I also said earlier, if you don‘t get the WPF version, dont worry, that will be the focus of another article.

I hope there are some of you, that has readPart1 andPart2 of this article series, and can see the simularities 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 didnt 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 3 at some stage on a live system. Happy LINQing.
I would just like to ask, if you liked the article please vote for it, and leave some comments, as it lets me know if the article was at the right level or not, and whether it contained what people need to know.
I 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 donw in XLINQ. No more iterative processes, hooray.
v1.0 10/05/07 : Initial issue
Concepts behind the C# 3.0 languageLINQ Project101 LINQ SamplesMay 2006 LINQ CTP
About Sacha Barber

I am currently doing an MSc at Sussex University in Information Technology for E-Commerce (ITEC). I currently hold a 1st class (or summa cum laude, if you like that sort of thing) BSc Hons degree from Sussex University, in Computer Science & Artificial Intelligence.
Award(s)
I was awarded the "Best IT project for 2006" award, at Sussex University, for my final year degree project, by the British Computer Society.
1st Place for C# monthly code project competition for thisarticle, in March 2007.
Almost But Not Quite Award(s)
2nd Place for ASP .NET monthly code project competition for thisarticle, in January 2007. The winner was Omar Al Zabir, for thisarticle.
2nd Place for C# monthly code project competition for thisarticle, in December 2006. The winner was Omar Al Zabir, for thisarticle.
2nd Place for C# monthly code project competition for thisarticle, in November 2006. The winner was Andrew Kirillov, for thisarticle.
Interests
I am quite interested in AI / computer vision / C# / web development / imaging / compilers and reflection. I also enjoy looking at new technologies such as LINQ / WPF. I just enjoy the learning process
Final Words
I would encourage people to write articles for codeproject, as having a real project is a really good way to pick up new skills. It has certainly helped me pick up new ideas, and new coding concepts. Go for it
My Blog
sachabarber.net Clickhere to view Sacha Barber‘s online profile.