Hello, I'm

Vijay Sharma

I'm a Full Stack Web Developer

An enthusiastic person currently shaping the future of software development by designing and developing smooth user interfaces that promote user interaction with information and data.

About Me

Efficient Data Extraction with LINQ in C#

In this article

You will get to know about...
→ LinQ
Introduction :

In the realm of C# development, managing and extracting data from Data sources like DataTables etc. is a common task. However, traditional approaches to data retrieval can often be cumbersome and verbose. Enter LINQ ( Language Integrated Query ), a powerful feature of C# that revolutionizes the way we work with collections and data sources.

In this blog post, we'll explore how LINQ simplifies data extraction from data sources, focusing on a specific scenario: retrieving the data from the data sources based on the real life scenarios. We'll delve into a concise LINQ query that elegantly handles this task, demonstrating the efficiency and readability that LINQ brings to C# development.

Join us as we unlock the potential of LINQ to streamline data manipulation, making our code more expressive and our development process more efficient. Let's dive in!

What is a query and what does it do? 🤔💭
A query is like 📋 a recipe you create to find specific information from a bunch of data. It's a bit like asking a friend for directions – you tell them what you want and how you want it.

Think of the data source as a big box of assorted items. In a database, it's like looking through rows of neatly stacked items. In an XML file, it's more like sifting through a stack of pages, each with different bits of information. And in an in-memory collection, it's as if you're sorting through a pile of objects, each with its own unique details.

But here's the thing: the query itself isn't the stuff you're looking for. It's more like the map 🗺️ that guides you to the treasure. It tells you where to look and how to organize what you find.

So, a query helps you find what you need from a bunch of data, kind of like asking for directions to a hidden treasure. 🕵️‍♂️💎
Language Integrated Query (LINQ)
Language-Integrated Query (LINQ) is like having a secret arsenal of superpowers embedded right into the heart of C# programming. It's not just about writing code; it's about wielding the power of queries seamlessly within your favorite language.

Traditionally, queries against data are expressed as simple strings without type checking at compile time or IntelliSense support.

Furthermore, you'd typically have to become fluent in a different query language for each type of data source: SQL databases, XML documents, various Web services, and so on. 📊📜🌐 But with LINQ, it's like having a universal translator built right into your C# toolkit. 🚀 No need to juggle multiple languages – LINQ empowers you to speak the language of data, no matter where it resides. 🗣💻

The following example shows a complete query operation. The complete operation includes creating a data source, defining the query expression, and executing the query in a foreach statement. 👇

// Data source: array of scores
int[] scores = { 97, 92, 81, 60 };

// Query to select scores greater than 80
var scoreQuery = from score in scores
                 where score > 80
                 select score;

// Execution of the query and output
foreach (var score in scoreQuery)
{
    Console.Write(score + " ");
}
// Output: 97 92 81

In this code : 👆

→ We utilize LINQ's where clause to filter scores greater than 80.
→ The query expression score > 80 acts as the condition to filter the scores.
→ Finally, we retrieve and process the filtered scores using a foreach loop.

Three Parts of a Query Operation
All LINQ query operations consist of three distinct actions:

1. Obtain the data source.
2. Create the query.
3. Execute the query.

// Let's Explore LINQ in Three Steps:

// Step 1: Setting Up the Numbers
// Imagine a bunch of numbers waiting for us to dig into.
int[] numbers = { 0, 1, 2, 3, 4, 5, 6 };

// Step 2: Crafting Our Search
// We're looking for certain numbers here - the even ones.
// 'numQuery' will help us find them.
var numQuery = from num in numbers
    		   where (num % 2) == 0
               select num;

// Step 3: Finding and Displaying
// Time to see what we've found!
// We'll loop through the results and print them out.
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

→ "{0,1}" : This is a format string. The {0} indicates where to insert the value of the variable num. The ,1 specifies the minimum field width as 1 character.

Data Source Overview
| CustomerId | Name     | City    |
|------------|----------|---------|
| 1          | John     | London  |
| 2          | Alice    | Paris   |
| 3          | Bob      | London  |
| 4          | Emma     | New York|
| 5          | Michael  | London  |
// Query to Retrieve Customers in London
IQueryable<Customer> customersInLondonQuery = from customer in database.Customers
    								          where customer.City == "London"
    							              select customer;

In this code : 👆

→ In the following example, Customers represents a specific table in the database, and the type of the query result, IQueryable, derives from IEnumerable.

Forcing Immediate Execution

Queries that perform aggregation functions, like Count, Max, Average, and First, need to iterate over the source elements to compute their result. These queries return a single value rather than a collection. For example, the following query counts the even numbers in the source array.

// Sample array of numbers
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Query to Count Even Numbers
var evenNumberQuery = from number in numbers
                      where (number % 2) == 0
                      select number;

// Counting Even Numbers
int evenNumberCount = evenNumberQuery.Count();

→ To force immediate execution of any query and cache its results, you can call the ToList or ToArray methods.
→ These methods compel the query to be executed instantly, storing the results in a list or array, respectively.
→ This can be particularly useful when you need to manipulate or iterate over the results multiple times without re-executing the query.

// Immediate Execution with ToList()
List<int> evenNumbersList = (from num in numbers
                        where (num % 2) == 0
                        select num).ToList();

// Immediate Execution with ToArray()
int[] evenNumbersArray = (from num in numbers
     			    where (num % 2) == 0
     			    select num).ToArray();

In these queries : 👆

→ We're using LINQ to filter even numbers from the numbers array.
→ The ToList() and ToArray() methods ensure immediate execution of the query and cache the results in a list or array, respectively.

You can also enforce query execution by immediately following the query expression with a foreach loop. However, utilizing ToList or ToArray not only triggers execution but also caches all the data within a single collection object. This can be advantageous when you need to reuse or manipulate the results multiple times efficiently.

Given this source sequence, a query might do one of three things : 👇

→ Retrieve a subset of the elements to produce a new sequence without modifying the individual elements.

// LINQ query to filter and sort high scores
IEnumerable highScoresQuery = from score in scores
								   where score > 80
								   orderby score descending
								   select score;

→ Retrieve a sequence of elements as in the previous example but transform them to a new type of object. The following example shows a projection from an int to a string. Note the new type of highScoresQuery.

// LINQ query to filter and format high scores
IEnumerable highScoresQuery2 = from score in scores
									   where score > 80
                                       orderby score descending
                                       select $"The score is {score}";

→ Retrieve a singleton value about the source data

// Counting high scores above 80
var highScoreCount = (from score in scores
                      where score > 80
                      select score).Count();
Starting a query expression 👇

→ Query expressions in C# begin with a "from" clause, defining a data source and a range variable.
→ The range variable iterates over each element in the source sequence during traversal.
→ The "from" clause specifies the data source and introduces a range variable.
→ The range variable is strongly typed according to the elements in the data source.
→ For instance, if the data source is an array of Country objects, the range variable is also typed as Country.

| CountryId | Name        | Population |
|-----------|-------------|------------|
| 1         | USA         | 10000      |
| 2         | China       | 5000       |
| 3         | India       | 10000      |
| 4         | Brazil      | 20000      |
| 5         | Pakistan    | 50000      |
| ...       | ...         | ...        |
// Query to filter countries with a population greater than 10 thousand and select their names
var query = from country in countries
            where country.Population > 10000
            select country.Name;

A query expression might contain multiple from clauses. Use more from clauses when each element in the source sequence is itself a collection or contains a collection. For example, assume that you have a collection of Country objects, each of which contains a collection of City objects named Cities. To query the City objects in each Country, use two from clauses as shown here:

IEnumerable<City> cityQuery = from country in countries
                                    from city in country.Cities
                                    where city.Population > 10000
                                    select city;
Ending a query expression 👇

A query expression must end with either a group clause or a select clause.

→ group clause

  • Use the group clause to produce a sequence of groups organized by a key that you specify.
  • The key can be any data type.
  • For example, the following query creates a sequence of groups that contains one or more Country objects and whose key is a char type with value being the first letter of countries' names.

  • Keep in mind that while these mechanisms provide a way to pass data from the controller to the view, they should be used judiciously, and the choice between them often depends on the specific requirements of your application. Also, using strongly-typed models (via Model) is a recommended practice for passing data to views in a more type-safe manner.

    Post a Comment

    0 Comments