Start querying with LINQ

  • Two fundamental building blocks of LINQ are the concepts of “elements” and “sequences”

Sequence

  • A sequence can be thought of as a list of items, with each item in the list being an element. A sequence is an instance of a class that implements the IEnumerable<T> interface.
    • Example for sequence

    int[] fibonacci = {0, 1, 1, 2, 3, 5};

  • sequence could be a local sequence of in-memory objects or a remote sequence
  • In the case of remote data sources (for example SQL Server), these remote sequences also implement the IQueryable<T> interface    

 

  • Queries that run on local sequences are known as local queries or LINQ-to-objects queries

     

  • Return value of query can be sequence or scalar value

     

     

int[] fibonacci = { 0, 1, 1, 2, 3, 5 };
// Scalar return value
int numberOfElements = fibonacci.Count();

Console.WriteLine(“Count: {0}”, numberOfElements);

 

// Output sequence return value

IEnumerable<int> distinctNumbers = fibonacci.Distinct();

Console.WriteLine(“Elements in output sequence:”);

 

foreach (var number in distinctNumbers)

{

Console.WriteLine(number);

}

 

Deferred execution

This means that the query does not execute when it is created, but when it is used or enumerated.

 

int[] fibonacci = { 0, 1, 1, 2, 3, 5 };

 

// Construct the query

IEnumerable<int> numbersGreaterThanTwoQuery = fibonacci.Where(x => x > 2);

 

// At this point the query has been created but not executed

 

// Change the first element of the input sequence

fibonacci[0] = 99;

 

// Cause the query to be executed (enumerated)

foreach (var number in numbersGreaterThanTwoQuery)

{

Console.WriteLine(number);

//query is executed when it’s called

}

 

Result

99

3

5

 

Exceptional cases

  • operators such as Count will cause the query to be executed immediately, and not deferred
  • There are a number of conversion operators that also cause immediate query execution, such as ToList, ToArray, ToLookup, and ToDictionary.

 

Lambda expressions in query operators

fibonacci.Where(x => x > 2)

Here the lambda expression x => x > 2 will only return elements (ints in this case) that are greater than 2.

 

Local and interpreted queries

LINQ provides for two distinct architectures: local and interpreted.

Local queries operate on IEnumerable<T> sequences and are compiled into the resulting assembly at compile time. Local queries, as the name suggests, can be thought of as operating on sequences local to the machine on which the query is executing (for example, querying an in-memory list of objects).

Interpreted queries are interpreted at runtime and work on sequences that can come from a remote source such as an SQL Server database. Interpreted queries operate on IQueryable<T> sequences.

There are two styles of writing LINQ queries:

  • Fluent style (or fluent syntax)
  • Query expression style (or query syntax)
  1. Fluent Style (extension methods)

Fluent syntax makes use of the query operator extension methods as defined in the static System.Linq.Enumerable class

Query operators can be used singularly, or chained together to create more complex queries

  1. Query Expression

Query expressions offer a syntactical nicety on top of the fluent syntax.

SQL like querying

 

Example:

namespace ConsoleApp

{


class
Ingredient

{


public
string Name { get; set; }


public
int Calories { get; set; }

}

 


//Main Class


class
Program

{


static
void Main(string[] args)

{


Ingredient[] ingredients = {


new
Ingredient { Name = “Sugar”, Calories = 500 },


new
Ingredient { Name = “Egg”, Calories = 100 },


new
Ingredient { Name = “Milk”, Calories = 150 },


new
Ingredient { Name = “Flour”, Calories = 50 },


new
Ingredient { Name = “Butter”, Calories = 200 }

};

 


#region Fluent style LINQ

 


IEnumerable<string> highCalorieIngredientNames =

ingredients.Where(x => x.Calories >= 150)

    .OrderBy(x => x.Name)

    .Select(x => x.Name);


//Ingredient object is transformed to a simple string. This transformation is called projection

 


//”x => x.Name” is called lambda expressions

 


foreach (var ingredientName in highCalorieIngredientNames) {


Console.WriteLine(ingredientName);

}

 

#endregion

 


#region Query expression style LINQ

 


IEnumerable<string> lowCalorieIngredientNames = from y in ingredients


where y.Calories <= 150


orderby y.Name


select y.Name;

 


foreach (var lowIngredient in lowCalorieIngredientNames) {


Console.WriteLine(lowIngredient);

}

 


#endregion

 


Console.ReadKey();

}

}

}

Range variables

 

  • Additional from clauses
  • The let clause

It is sometimes useful to store the result of a sub-expression in order to use it in subsequent clauses

 


IEnumerable<Ingredient> highCalDairyQuery =


from i in ingredients


let isDairy = i.Name == “Milk” || i.Name == “Butter”


where i.Calories >= 150 && isDairy


select i;

// isDairy keeps temporary value stored

 

 


//let can also be used to introduce a subsequence


string[] csvRecipes = { “milk,sugar,eggs”,


“flour,BUTTER,eggs”,


“vanilla,ChEEsE,oats” };

 


var dairyQuery = from csvRecipe in csvRecipes


let csvingredients = csvRecipe.Split(‘,’)


from ingredient in csvingredients //multiple from clause


let uppercaseIngredient = ingredient.ToUpper()


where uppercaseIngredient == “MILK”

|| uppercaseIngredient == “BUTTER”

|| uppercaseIngredient == “CHEESE”


select uppercaseIngredient;

 


foreach (var dairyIngredient in dairyQuery) {


Console.WriteLine(“{0} is dairy”, dairyIngredient);

}

 

  • The into keyword

To be declared that can store the result of a select clause (as well as group and join clauses)


IEnumerable<Ingredient> highCalDairyQuery =


from i in ingredients


select
new
// anonymous type

{

OriginalIngredient = i,

IsDairy = i.Name == “Milk” || i.Name == “Butter”,

IsHighCalorie = i.Calories >= 150

}


into temp


where temp.IsDairy && temp.IsHighCalorie


// cannot write “select i;” as into hides the previous range variable i


select temp.OriginalIngredient;

  • The join clause

The join clause takes two input sequences in which elements in either sequence do not necessarily have any direct relationship in the class domain model.

Common types of joins include:

 Inner joins.

 Group joins.

 Left outer joins.

Inner Join

//INNER JOIN


Recipe[] recipes = {


new
Recipe { Id = 1, Name = “Mashed Potato” },


new
Recipe { Id = 2, Name = “Crispy Duck” },


new
Recipe { Id = 3, Name = “Sachertorte” }};

 


Review[] reviews = {


new
Review { RecipeId = 1, ReviewText = “Tasty!” },


new
Review { RecipeId = 1, ReviewText = “Not nice :(“ },


new
Review { RecipeId = 1, ReviewText = “Pretty good” },


new
Review { RecipeId = 2, ReviewText = “Too hard” },


new
Review { RecipeId = 2, ReviewText = “Loved it” }

};

 


var query = from recipe in recipes


join review in reviews


on recipe.Id equals review.RecipeId


select
new
// anonymous type

{

RecipeName = recipe.Name,

RecipeReview = review.ReviewText

};

 


foreach (var item in query) {


Console.WriteLine(“{0} – ‘{1}'”, item.RecipeName, item.RecipeReview);

}

Group join

//group join


var query = from recipe in recipes


join review in reviews


on recipe.Id equals review.RecipeId


into reviewGroup


select
new
// anonymous type

{

RecipeName = recipe.Name,

Reviews = reviewGroup // collection of related reviews

};

 


foreach (var item in query) {


Console.WriteLine(“Reviews for {0}”, item.RecipeName);

 


foreach (var review in item.Reviews) {


Console.WriteLine(” – {0}”, review.ReviewText);

}

}

    

 


 


Advertisements

LINQ simple sample for beginners

class Program
{

static void Main(string[] args)
{
//Collection
var primes = new List<int> { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 };

//SQL like quering
IEnumerable<int> query = from val in primes
where val < 13
select val;

foreach (var val in query)
Console.WriteLine(val);

//using expressions
var methodQuery = primes.Where(x => x < 13);

foreach (var val in methodQuery)
Console.WriteLine(val);

}
}

}
}