What LINQ to XML Replaces
Before LINQ to XML, working with XML in .NET meant using the verbose DOM-style XmlDocument API, navigating nodes with XPath strings, and manually casting XmlNode objects, all with weak compile-time checking. LINQ to XML introduces XDocument and XElement, an in-memory tree representation designed to be constructed and queried with ordinary LINQ operators and object-initializer syntax, so the code that builds or reads XML looks like idiomatic C# rather than string-heavy XPath navigation.
Cricket analogy: Reading an old-fashioned paper scorebook with hand-written tally marks compared to a modern digital scoring app that lets you tap a player's name to instantly filter their stats mirrors the shift from XmlDocument's XPath strings to XElement's LINQ queries.
XDocument and XElement Basics
XDocument.Load(path) or XDocument.Parse(xmlString) reads an entire XML document into an in-memory tree of XElement and XAttribute objects, where each XElement exposes Elements() and Attributes() to navigate children and Element("Name") to get the first matching child directly. Building new XML is equally direct with functional construction: nesting new XElement("name", content) calls inside each other mirrors the shape of the resulting document, which reads far more naturally than the older API's sequence of CreateElement and AppendChild calls.
Cricket analogy: A team's full playing squad list with each player's role nested underneath the team name mirrors how XElement nests child elements under a parent, letting you navigate from team to players directly.
// Building XML with functional construction
var doc = new XDocument(
new XElement("catalog",
new XElement("book",
new XAttribute("id", "bk101"),
new XElement("title", "LINQ in Action"),
new XElement("author", "Jane Doe"),
new XElement("price", 39.99)
),
new XElement("book",
new XAttribute("id", "bk102"),
new XElement("title", "C# Deep Dive"),
new XElement("author", "John Smith"),
new XElement("price", 44.99)
)
)
);
// Reading it back and navigating
var firstTitle = doc.Root.Element("book").Element("title").Value;
doc.Save("catalog.xml");Querying XML with LINQ
Because XElement.Elements() returns IEnumerable<XElement>, the full LINQ operator set applies directly: doc.Descendants("book").Where(b => (decimal)b.Element("price") > 40).Select(b => (string)b.Element("title")) filters and projects XML content using the same query syntax you'd use on any other collection. XElement and XAttribute also support explicit conversion operators to primitive types like decimal, int, and DateTime, which is why casting (decimal)b.Element("price") reads the element's text content and parses it directly, avoiding manual string parsing.
Cricket analogy: Filtering a full ball-by-ball database down to just deliveries bowled by spinners that went for a boundary uses the same query pattern whether the data comes from a database or an XML scorecard feed, just like Descendants().Where() on XML.
Modifying and Creating XML
Existing XElement objects can be modified in place with Add() to append a new child, SetElementValue() or SetAttributeValue() to update or create a value in one call, and Remove() to delete an element or attribute from its parent, all of which mutate the live in-memory tree directly. Because these methods mutate the tree, and because navigation methods like Descendants() are themselves lazily-evaluated LINQ queries, modifying the tree while iterating over a live Descendants() query (for example calling Remove() inside a foreach over doc.Descendants("book")) throws an InvalidOperationException from a collection-modified-during-enumeration error, so such operations should materialize the query with .ToList() first.
Cricket analogy: Editing a live scorecard app by adding a new batter's entry while someone else is simultaneously scrolling through that same live-updating list causes the display to jump unpredictably, similar to modifying an XElement tree while a live Descendants() query enumerates it.
Calling .Remove() or .Add() on elements while foreach-ing over a live Descendants() or Elements() query throws an InvalidOperationException, because that query is itself a lazily-evaluated LINQ enumerable over the live tree. Always call .ToList() on the query first to snapshot the elements you intend to modify, then mutate the tree freely.
// Safe mutation: materialize before modifying
var cheapBooks = doc.Descendants("book")
.Where(b => (decimal)b.Element("price") < 40)
.ToList(); // snapshot first
foreach (var book in cheapBooks)
{
book.SetElementValue("onSale", true);
book.SetAttributeValue("discount", "10%");
}
// Removing elements safely
doc.Descendants("book")
.Where(b => (string)b.Element("author") == "Unknown")
.ToList()
.ForEach(b => b.Remove());- XDocument and XElement replace the older XmlDocument DOM API with an in-memory tree built and queried using ordinary LINQ.
- XDocument.Load() and XDocument.Parse() read XML into memory; functional construction with nested new XElement() calls builds it.
- XElement.Elements() and Descendants() return IEnumerable<XElement>, so the full LINQ operator set (Where, Select, OrderBy) applies directly.
- Explicit conversion operators let you cast an XElement or XAttribute directly to decimal, int, DateTime, and other primitive types.
- SetElementValue(), SetAttributeValue(), Add(), and Remove() mutate the live tree in place.
- Modifying the tree while enumerating a live Descendants() or Elements() query throws an InvalidOperationException.
- Always materialize the target elements with .ToList() before calling Remove() or Add() inside a loop over a query.
Practice what you learned
1. What is the main advantage of XElement/XDocument over the older XmlDocument DOM API?
2. What does casting an XElement directly to decimal, as in (decimal)b.Element("price"), do?
3. Why does calling .Remove() inside a foreach loop over doc.Descendants("book") throw an InvalidOperationException?
4. What is the correct way to safely remove a set of elements found by a LINQ query over an XML tree?
5. Which method would you use to build an XML document from scratch using functional construction with LINQ to XML?
Was this page helpful?
You May Also Like
Debugging LINQ Queries
Practical techniques for stepping through, inspecting, and diagnosing bugs in LINQ query chains, including deferred execution pitfalls.
LINQ vs Loops
Compare LINQ's declarative query syntax against traditional for/foreach loops in terms of readability, composability, and raw performance.
LINQ Performance Pitfalls
Learn the hidden costs of LINQ's declarative syntax, from multiple enumeration to deferred-execution surprises, and how to avoid them.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics