Crash course: LINQ in 10 minutes

In code reviews, I unfortunately often see that LINQ is relatively rarely used. If I ask why, I often hear that this programming concept is not known or not well known and the software developer feel uncertain on using LINQ. And sometimes I also implement data analysis in good old foreach loops instead using LINQ.

If you also do not know LINQ or just know the basics, then just take ten minutes and read this article. Within this crash course I’ll give you a brief overview of the basic LINQ features. After reading this article, you have enough knowledge to be able to use LINQ in your projects immediately.

For the crash course a basic understanding of SQL queries is required. You should be familiar with terms such as: select, from, where, group, join.

 
Introduction

LINQ is shortcut for Language Integrated Query. It is a powerful language extension for the analysis of data sets. The datasets can come from different sources such as objects, XML files or databases. LINQ provides a declarative syntax which has similarities with SQL. This allows to easily create data analysis and it results in a good understandable source code.

 
Data source for the sample applications

Before I can show you the first LINQ query, we need a data source. For the sample queries in this crash course, objects are used as the data source. The following code snippet shows the data, which are then used in all examples.

    1 private List<Customer> _customers;

    2 private List<Order> _orders;

    1 _customers = new List<Customer>()

    2 {

    3     new Customer() { Identifier = 1, FirstName = "Hans", LastName = "Meier" },

    4     new Customer() { Identifier = 2, FirstName = "Uwe", LastName = "Mueller" },

    5     new Customer() { Identifier = 3, FirstName = "Sandra", LastName = "Schmidt" }

    6 };

    7 

    8 _orders = new List<Order>()

    9 {   

   10     new Order() { Identifier = 101, CustomerIdentifier = 2, TotalPrice = 750 },

   11     new Order() { Identifier = 102, CustomerIdentifier = 2, TotalPrice = 80 },

   12     new Order() { Identifier = 103, CustomerIdentifier = 2, TotalPrice = 120 },

   13     new Order() { Identifier = 104, CustomerIdentifier = 3, TotalPrice = 55 }

   14 };

 
Two lists are created. A customer list and a list of orders. In the orders you will find a customer number. Thus a link between the two lists can be created.

 
Basic data queries

Now I will show you a first LINQ query. It should query all orders, which Mr. Miller has made. In this basic example we will use the identification number of the customer directly and don’t want to create a connection to the customer list.

    1 var results = from order in _orders

    2               where order.CustomerIdentifier == 2

    3               select order.TotalPrice;

    4 

    5 foreach (var result in results)

    6 {

    7     Console.WriteLine(result);

    8 }

 
The following output is shown:

750

80

120

 
As you can see the syntax is very much like in a SQL query. All elements are selected from the _orders list if they have the corresponding customer number. From this filtered elements, only the value TotalPrice is selected. Therefore the query returns a new list with all order prices of orders made by this customer.

The declarative syntax in LINQ is just syntactic sugar to sweeten the software developer’s work a little bit. In the background, the query is converted into functional code. Instead of declarative syntax, the query can therefore also be written using functions. The following code snippet shows the same query as before, but this time in functional notation.

    1 var results = _orders.Where(order => order.CustomerIdentifier == 2)

    2                      .Select(order => order.TotalPrice);

 
On the basis of this source code you may can imagine how LINQ is programmed. LINQ is implemented as a fluent interface which uses Extension Methods and Lambda Expressions. For the understanding of this article it is not required that you are familiar with this programming concept.

 
As you can see in the two examples, you can implement declarative or functional in LINQ. The declarative syntax, however, is clearly to be preferred because it results in a more readable source code. But a declarative statement but also be extended with functional code. The following example shows such a mixed case.

    1 var results = (from order in _orders

    2                where order.CustomerIdentifier == 2

    3                select order.TotalPrice)

    4                .ToArray();

 
Query result type

In the previous examples, the value to be transferred from the data set in the result list was given after the select keyword. Only the value TotalPrice was selected. Of course it is also possible to select other values. In principle, the values ​​can be combined in various combinations. The code below shows three examples. In the first case, the complete customer element is selected, in the second example only one value from the customer element is queried and in the third case a completely new element is created which contains the values ​​of costs and taxes.

    1 var results1 = from order in _orders

    2                where order.CustomerIdentifier == 2

    3                select order;

    4 

    5 var results2 = from order in _orders

    6                where order.CustomerIdentifier == 2

    7                select order.TotalPrice;

    8 

    9 var results3 = from order in _orders

   10                where order.CustomerIdentifier == 2

   11                select new

   12                {

   13                    Cost = order.TotalPrice,

   14                    Taxes = order.TotalPrice * 0.07

   15                };

 
The query results can therefore be freely defined. The LINQ queries will generate anonymous types.

 
Sorting of query results

Just as with SQL also with LINQ the question arises how the results are sorted. If you do not specify sorting then the results are sorted in both cases randomly. If you expect the results in a certain order, then you must specify this in the LINQ statement. You can do so by using the orderby command. In the following example, the data is first sorted with ascending and then descending order by price.

    1 var resultsAsc = from order in _orders

    2                  where order.CustomerIdentifier == 2

    3                  orderby order.TotalPrice

    4                  select order;

    5 

    6 var resultsDesc = from order in _orders

    7                   where order.CustomerIdentifier == 2

    8                   orderby order.TotalPrice descending

    9                   select order;

 
Grouping

In SQL and also in LINQ you can combine the results of data queries together into groups. Let us look at the list of orders. A typical use case would be: Determine how many orders each customer has placed. The code below shows the corresponding LINQ statement which demonstrates grouping by using the keywords group/by/into.

    1 var results = from order in _orders

    2               group order by order.CustomerIdentifier into g

    3               select new { Identifier = g.Key, Count = g.Count() };

    4 

    5 foreach (var result in results)

    6 {

    7     Console.WriteLine(result.Identifier + ": " + result.Count);

    8 }

 
The following output is generated:

2: 3

3: 1

 
Join data

Till now the two lists of data have been used only separately. However, the list of orders contains the customer number. So a connection between the two data sets exists. Look at the following example: You want to know all orders, their prices and the customer of the order. The code below shows a possible implementation which connects both data lists.

    1 var results = from customer in _customers

    2               join order in _orders on

    3                 customer.Identifier equals order.CustomerIdentifier

    4               select new

    5               {

    6                   Name = customer.LastName + ", " + customer.FirstName,

    7                   order.TotalPrice

    8               };

    9 

   10 foreach (var result in results)

   11 {

   12     Console.WriteLine(result.Name + ": " + result.TotalPrice);

   13 }

 
Line 2 contains the information how the two lists should be linked. This is implemented with the keywords join/in/on/equals. In this case, the link is made by using the two Identifier values. Then the results will be selected in a new anonymous object.

 
The following output is generated:

Mueller, Uwe: 750

Mueller, Uwe: 80

Mueller, Uwe: 120

Schmidt, Sandra: 55

 
Aggregate functions

LINQ supports the typical aggregate functions known from SQL, such as: count, sum, min, max and average. The following example shows how these functions can be used.

 

    1 var count = (from order in _orders

    2              where order.CustomerIdentifier == 2

    3              select order.TotalPrice).Count();

    4 

    5 var min = (from order in _orders

    6            where order.CustomerIdentifier == 2

    7            select order.TotalPrice).Min();

    8 

    9 var max = (from order in _orders

   10            where order.CustomerIdentifier == 2

   11            select order.TotalPrice).Max();

   12 

   13 var sum = (from order in _orders

   14            where order.CustomerIdentifier == 2

   15            select order.TotalPrice).Sum();

   16 

   17 var average = (from order in _orders

   18                where order.CustomerIdentifier == 2

   19                select order.TotalPrice).Average();

   20 

   21 Console.WriteLine(count);

   22 Console.WriteLine(min);

   23 Console.WriteLine(max);

   24 Console.WriteLine(sum);

   25 Console.WriteLine(average);

 

The following output is generated:

3

80

750

950

316.6

 
Conversion to list, array or dictionary

The results of a LINQ statement can be converted to List, Array or Dictionary objects. Below you will see a corresponding example which shows how to use the commands ToArray, ToList and ToDictionary.

 

    1 var array = (from order in _orders

    2              where order.CustomerIdentifier == 2

    3              select order).ToArray();

    4 

    5 var list = (from order in _orders

    6             where order.CustomerIdentifier == 2

    7             select order).ToList();

    8 

    9 var dictionary = (from order in _orders

   10                   where order.CustomerIdentifier == 2

   11                   select order).ToDictionary(order => order.Identifier);

 

A dictionary always requires a key value. Therefore in line 11, the identifier of the order is given as a key for the dictionary.

 
Delayed execution vs. immediate execution

LINQ statements often return enumerable objects. In this case a LINQ statement is not executed immediately. It will not be executed until you iterate over the enumerable result. Only if you convert the result to another object, like in the previous example with the ToArray, ToList or ToDictionary, the LINQ statement will be executed immediate because in this case the results will already be used for the conversion and therefore an internal iteration over the results will be done. A statement can thus be executed delayed or immediately. Delayed LINQ queries are reusable.

The following example shows a query to get all prices of orders. After the query is performed and the results were read, a further order will be added to the list. Afterwards, the LINQ query is executed again. Due to the delayed execution of the statement, the new order will be included in the result of the second query execution.

 

    1 var query = from order in _orders

    2             where order.CustomerIdentifier == 2

    3             select order.TotalPrice;

    4 

    5 foreach (var result in query)

    6 {

    7     Console.WriteLine(result);

    8 }

    9 

   10 Console.WriteLine("——");

   11 

   12 _orders.Add(new Order()

   13 {

   14     Identifier = 104,

   15     CustomerIdentifier = 2,

   16     TotalPrice = 555

   17 });

   18 

   19 foreach (var result in query)

   20 {

   21     Console.WriteLine(result);

   22 }

 

The following output is generated:

750

80

120

——

750

80

120

555

 

If you add a conversion to the above LINQ query, for example by using ToList, then the query is executed immediately. The following sample shows the modified query.

 

    1 var query = (from order in _orders

    2              where order.CustomerIdentifier == 2

    3              select order.TotalPrice).ToList();

 

This modification results in an immediately query execution and creation of the list with result values. Thus, in the two foreach loops, only the results already generated is evaluated. Therefore, the newly inserted order is not included in the second loop.

 
The changed query creates the following output:

750

80

120

——

750

80

120

 
Summary

LINQ is a powerful language extension for the analysis of data sets. Complex data queries can be easily created and executed. LINQ allows using a declarative syntax, which results in clean and easy to understand source code.

Werbung
Dieser Beitrag wurde unter .NET, C#, Crashkurs abgelegt und mit verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit deinem WordPress.com-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s