LINQ

Much of modern development requires the use of multiple languages, specifically, a language for business logic, a language for presentation layers, and a query language for database operations.

Beyond extra demands on developers for proficiency in several languages, this causes major conflicts between languages in the application; for example, a compiler may not check a query string referencing columns and rows to see if they exist because the compiler views it only as a string.

LINQ, Language-Integrated Query, allows for set-based queries within application code, which avoids the use of a separate language. It allows for queries to various data sources including XML documents, SQL databases, DataSet objects, and more. These sources implement in different ways, but all use identical syntax and language constructs.

Queries in the native language allow code to avoid issues like embedded string literals not understood by the compiler. They also allow more productive development because developers spend less time query debugging, and utilize compile-time type and syntax checking. Other tasks like moving SQL tables into in-memory objects proves tedious and error-prone. The LINQ provider offered by LINQ to DataSet combined with LINQ to SQL provides the same functionality without heavy risk.

USAGE

The most visible aspect of LINQ integration is the query expression. Expressions conform to a declarative syntax. This syntax allows for rich querying including filtering, grouping, and ordering operations with minimal code. The same basic expression patterns are utilized to query and manipulate data whether SQL databases, XML documents and streams, ADO.NET datasets, or .NET collections.

The C# example below demonstrates the entire query operation process, which consists of spawning a data source, defining the expression, and executing the query:

class LINQExpr
	{
	static void Main()
	{
		// Data source
		int[] points = new int[] { 93, 82, 80, 50 };
						
		// Query
		IEnumerable<int> pointsQuery =
			from point in points
			where point > 80
			select point;
						
		// Execute query
		foreach (int i in pointsQuery)
		{
			Console.Write(i + " ");
		}
	}
}

LINQ queries retrieve from a data source, and the application sees the source as an IEnumberable<T> or IQueryable <T> collection regardless of its type. Queries all perform one of three possible tasks:

  1. It retrieves an element subset for producing a new sequence without changing individual elements.
  2. It retrieves a sequence of elements, and converts them into a new type of object.
  3. It retrieves a singleton value describing the source data.

Query expressions are first-class constructs; thus, they can be used in any context like any other C# or VB element. LINQ expressions conform to a declarative syntax resembling SQL or Xquery. They consist of a set of clauses, and each clause contains one or more C# expressions; which may be query expressions or contain them.

Query expressions must start with a from clause and end with a select or group clause. It can contain one or more optional clauses between its start and end. These optional clauses include where, orderby, join, let, and additional from clauses. The into keyword allows results of group or join to source additional query clauses within the expression.

In LINQ, query variables store queries, not results. These variables are always of enumerable type, which produces a sequence of elements when iterated through using a foreach statement, or a direct call to its Ienumerator.MoveNext method. Review a C# example of a query variable (or simply a query) below:

static void Main()
{
	// Data source
	int[] points = { 110, 105, 81, 88, 120, 94 };

	// Query
	IEnumerable<int> pointQuery = //query variable
		from point in points
		where point > 100
		orderby point descending
		select point;

	// Execute the query
	foreach (int gamePoints in pointQuery)
	{
		Console.WriteLine(gamePoints);
	}
}

The following C# example shows variables initialized with queries, which are not query variables:

int hiPoints =
	(from point in points
	select point)
	.Max();

//Another option splits the expression
IEnumerable<int> pointQuery =
	from point in points
	select point;

int hiPoints = pointQuery.Max();

LINQ TECHNOLOGIES

Four important LINQ technologies offered by ADO.NET include LINQ to Objects, LINQ to DataSet, LINQ to SQL, and LINQ to Entities.

DATASET and OBJECTS

LINQ to DataSet and LINQ to Objects provide a means of querying data cached in a DataSet object and querying objects directly without using a provider. Each tool minimizes and simplifies the code required to manage collections. They also improve readability, portability, filtering, grouping, and ordering; all with minimal coding.

The DataSet tool utilizes the extension methods of the DataRowExtensions and DataTableExtensions classes. It does not replace ADO.NET. All ADO.NET code within applications using LINQ to DataSet functions normally.

In practice, first populate the DataSet, typically with the DataAdapter class or LINQ to SQL. Then execute queries. Queries resemble most LINQ queries. Query variables store commands when a query must return a value sequence, however, if the query does not contain a method forcing immediate execution, the query remains deferred until iteration by a foreach loop. Another approach for queries utilizes methods. These queries employ a sequence of direct method calls and pass Lambda expressions as parameters.

LINQ to Objects refers specifically to the direct (no provider) use of queries with IEnumberable or IEnumerable<T> collections. These collections result from APIs or user-defined sources. This tool replaces the old method of using complex foreach looping to retrieve from a collection. It uses declarative code describing the target of the retrieval operation.

SQL AND ENTITIES

LINQ offers LINQ to SQL for management of relational data as objects. It translates languageintegrated queries into SQL for querying data in an SQL Server database. The operations supported include updates, deletion, and insertion. It resembles LINQ to Objects and LINQ to DataSet in terms of its support for minimizing and simplifying code. It improves readability, portability, and more.

Querying rules resemble those of standard queries meaning deferred or immediate execution. Queries rely on various components for execution:

  1. LINQ to SQL API
  2. LINQ to SQL Provider
  3. ADO Provider

In practice, connect to the data source using the DataContext class before executing any queries.

LINQ offers LINQ to Entities for management of relational data as objects. It translates languageintegrated queries into SQL for querying data in various databases or providers such as PostgreSQL, Oracle, and MySQL. This tool also offers more flexibility than LINQ to SQL allowing changes to queried data details, batch updates, and more. Its query operators include the same group as standard operators, e.g., Join, OrderBy, and more.