Practical LINQ Operations with DataSets and XML Documents

LINQ to DataSet

The DataSet family of objects—DataTable, DataRow, and DataColumn—forms the backbone of data access in many .NET applications. LINQ provides powerful querying capabilities over these objects, enabling developers to write type-safe, expressive queries against tabular data.

Required Namespaces

using System.Data;
using System.Linq;

Converting DataTable to Queryable Sequence

The AsEnumerable() extension method transforms a DataTable into an IEnumerable<DataRow> sequence, opening the door to LINQ operations.

var dataTable = new DataTable();
dataTable.Columns.Add("Id", typeof(int));

var firstRow = dataTable.NewRow();
var secondRow = dataTable.NewRow();

firstRow["Id"] = 100;
secondRow["Id"] = 200;

dataTable.Rows.Add(firstRow);
dataTable.Rows.Add(secondRow);

IEnumerable<DataRow> rowSequence = dataTable.AsEnumerable();

foreach (DataRow currentRow in rowSequence)
    Console.WriteLine($"Value: {currentRow["Id"]}");

This conversion is essential because DataTable itself does not implement standard LINQ query operators.

Set Operations on DataRow Sequences

Distinct — Removing duplicate rows requires DataRowComparer.Default, as standard equality comparison compares references rather than values.

var table = new DataTable();
table.Columns.Add("Score", typeof(int));

table.Rows.Add(new object[] { 85 });
table.Rows.Add(new object[] { 92 });
table.Rows.Add(new object[] { 85 });

IEnumerable<DataRow> allRows = table.AsEnumerable();
IEnumerable<DataRow> uniqueRows = allRows.Distinct(DataRowComparer.Default);

foreach (var r in uniqueRows)
    Console.WriteLine(r["Score"]);

// Output:
// 85
// 92

Except — Finding rows present in one sequence but not another:

var sourceTable = new DataTable();
sourceTable.Columns.Add("Value", typeof(int));

var filterTable = new DataTable();
filterTable.Columns.Add("Value", typeof(int));

sourceTable.Rows.Add(new object[] { 10 });
sourceTable.Rows.Add(new object[] { 20 });
sourceTable.Rows.Add(new object[] { 30 });

filterTable.Rows.Add(new object[] { 20 });
filterTable.Rows.Add(new object[] { 40 });

IEnumerable<DataRow> source = sourceTable.AsEnumerable();
IEnumerable<DataRow> exclusion = filterTable.AsEnumerable();

IEnumerable<DataRow> difference = source.Except(exclusion, DataRowComparer.Default);

foreach (var r in difference)
    Console.WriteLine(r["Value"]);

// Output:
// 10
// 30

Intersect — Retrieving rows common to both sequences:

var leftTable = new DataTable();
leftTable.Columns.Add("Item", typeof(string));

var rightTable = new DataTable();
rightTable.Columns.Add("Item", typeof(string));

leftTable.Rows.Add(new object[] { "Alpha" });
leftTable.Rows.Add(new object[] { "Beta" });

rightTable.Rows.Add(new object[] { "Beta" });
rightTable.Rows.Add(new object[] { "Gamma" });

IEnumerable<DataRow> left = leftTable.AsEnumerable();
IEnumerable<DataRow> right = rightTable.AsEnumerable();

IEnumerable<DataRow> common = left.Intersect(right, DataRowComparer.Default);

foreach (var r in common)
    Console.WriteLine(r["Item"]);

// Output:
// Beta

Union — Combining sequences while removing duplicates:

var setA = new DataTable();
setA.Columns.Add("Code", typeof(int));

var setB = new DataTable();
setB.Columns.Add("Code", typeof(int));

setA.Rows.Add(new object[] { 1 });
setA.Rows.Add(new object[] { 2 });

setB.Rows.Add(new object[] { 2 });
setB.Rows.Add(new object[] { 3 });

IEnumerable<DataRow> groupA = setA.AsEnumerable();
IEnumerable<DataRow> groupB = setB.AsEnumerable();

IEnumerable<DataRow> merged = groupA.Union(groupB, DataRowComparer.Default);

foreach (var r in merged)
    Console.WriteLine(r["Code"]);

// Output:
// 1
// 2
// 3

SequenceEqual — Comparing two sequences for identical content:

var firstTable = new DataTable();
firstTable.Columns.Add("Data", typeof(int));

var secondTable = new DataTable();
secondTable.Columns.Add("Data", typeof(int));

firstTable.Rows.Add(new object[] { 5 });
firstTable.Rows.Add(new object[] { 10 });

secondTable.Rows.Add(new object[] { 5 });
secondTable.Rows.Add(new object[] { 10 });

IEnumerable<DataRow> seq1 = firstTable.AsEnumerable();
IEnumerable<DataRow> seq2 = secondTable.AsEnumerable();

bool areIdentical = seq1.SequenceEqual(seq2, DataRowComparer.Default);
Console.WriteLine(areIdentical);

// Output:
// True

Working with DataColumn Values

Field<T> — A strongly-typed extension method for retrieving column values:

var sampleTable = new DataTable();
sampleTable.Columns.Add("Price", typeof(decimal));

sampleTable.Rows.Add(new object[] { 29.99m });
sampleTable.Rows.Add(new object[] { 49.99m });

IEnumerable<DataRow> rows = sampleTable.AsEnumerable();

IEnumerable<decimal> prices = rows.Select(r => r.Field<decimal>("Price"));

foreach (decimal p in prices)
    Console.WriteLine(p);

// Output:
// 29.99
// 49.99

The method supports three parameter overloads: DataColumn, string, and int. Using the string overload improves code readability.

SetField<T> — Updating column values in place:

var numbers = new DataTable();
numbers.Columns.Add("Amount", typeof(int));

numbers.Rows.Add(new object[] { 5 });
numbers.Rows.Add(new object[] { 15 });

IEnumerable<DataRow> dataRows = numbers.AsEnumerable();

foreach (DataRow row in dataRows)
    row.SetField<int>("Amount", row.Field<int>("Amount") * 2);

foreach (int val in dataRows.Select(r => r.Field<int>("Amount")))
    Console.WriteLine(val);

// Output:
// 10
// 30

Creating a New DataTable from Query Results

The CopyToDataTable() method reconstructs a DataTable from a sequence of DataRow objects:

var original = new DataTable();
original.Columns.Add("Name", typeof(string));
original.Rows.Add(new object[] { "Alice" });
original.Rows.Add(new object[] { "Bob" });

IEnumerable<DataRow> filtered = original.AsEnumerable()
    .Where(r => r.Field<string>("Name").StartsWith("A"));

DataTable filteredTable = filtered.CopyToDataTable();

LINQ to XML

The System.Xml.Linq namespace provides a fluent API for constructing, querying, and manipulating XML documents. These types integrate seamlessly with standard LINQ operators.

Required Namespaces

using System.Linq;
using System.Xml.Linq;

Core Types

Type Purpoce
XDocument Represents an entire XML document
XElement Represents a single XML element
XAttribute Represents an XML attribute
XNamespace Manages XML namespace prefixes
XCData Encapsulates CDATA sections
XDeclaration Specifies XML version and encoding

Building XML Documents Programmatically

XDocument document = new XDocument(
    new XDeclaration("1.0", "utf-8", "yes"),
    new XElement("catalog",
        new XElement("book",
            new XAttribute("id", "B001"),
            "Professional C#"),
        new XElement("book",
            new XAttribute("id", "B002"),
            new XCData("<script>alert('test')</script>"))));

// Serialized output:
// <?xml version="1.0" encoding="utf-8" standalone="yes"?>
// <catalog>
//   <book id="B001">Professional C#</book>
//   <book id="B002"><![CDATA[<script>alert('test')</script>]]></book>
// </catalog>

Persisting and Loading Documents

Saving to disk:

document.Save("catalog.xml");

Loading from file:

XDocument loaded = XDocument.Load("catalog.xml");

The Load method supports multiple overloads accepting Stream, String, TextReader, or XmlReader.

XNode Hierarchy

XNode serves as the abstract base for all XML content nodes, including comments, text, and processing instructions. Its derived class XContainer encompasses XDocument and XElement, which can hold child nodes.

Traversal Methods

XDocument doc = XDocument.Load("catalog.xml");

// All descendant nodes (elements, text, comments, etc.)
IEnumerable<XNode> allNodes = doc.DescendantNodes();

// Direct child elements only
IEnumerable<XElement> childElements = doc.Elements();

Once you obtain node or element sequences, standard LINQ operators become available:

XDocument doc = XDocument.Load("catalog.xml");

IEnumerable<string> bookIds = doc
    .Descendants("book")
    .Where(e => e.Value.Length > 10)
    .Select(e => e.Attribute("id")?.Value);

foreach (string id in bookIds)
    Console.WriteLine(id);

For comprehensive API documentation, consult the System.Xml.Linq namespace reference in the Microsoft documentation.

Tags: LINQ Dataset DataTable XML XDocument

Posted on Tue, 08 Sep 2026 16:27:27 +0000 by mbariou