8. LINQ Queries || C# 10 Flashcards

1
Q

What is a LINQ?

  1. What it’s description?
  2. What kind of collection you can use LINQ on?
  3. In which namespace is it defined?
A
  1. LINQ (Language Integrated Query) is a set of language and runtime features for writing structured and type-safe queries over local object collections and remote data sources
  2. LINQ enables you to query any collection implementing IEnumerable<T>, whether an array, list or XML Document Object Model, as well as remote data sources (tables in SQL database)
  3. System.Linq and System.Linq.Expressions
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

What is a basic units of data in LINQ? Describe that

A
  1. Sequences - any object that implements IEnumerable<T>
  2. Elements - each item in the sequence

string[] names = {"Tom", "Dick", "Harry"};
names - sequence
“Tom”, “Dick” and “Harry” - elements

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q
  1. What is a query operator?
  2. What are the members of the typical query operator?
  3. What is a standart query operator?
A

Query operator - a method that transforms a sequence. A typical query operator accepts an input sequence and emits a transformed output sequence.
Standart query operator is one of the 40 query operators that are already defined in Enumerable class in System.Linq

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

What is a query?

A

Query is an expression that, when enumerated, transforms sequences with query operators. The simples query comprises one input sequence and one operator:
~~~
using System.Linq;

string[] names = {“Tom”, “Dick”, “Harry”};
IEnumerable<string> filteredNames = Enumerable.Where(names, n => n.Length >= 4);
~~~
or simply
~~~
names.Where(w => w.Length >= 4);
~~~</string>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

What is a fluent syntax in LINQ?

A

Fluent syntax is the most flexible and fundamental. It is achieved by chaining query operators one after another and the output of one operator is the input of the next:
~~~
var names = {“Tom”, “Jim”, “Harry”};
var query = names
.Where(w => w.Contains(“a”))
.OderBy(o => o.Length)
.Select(s => s.ToUpper());
~~~
// HARRY

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

How the compiler would translate this?
~~~
var names = {“Tom”, “Jim”, “Harry”};
var query = names
.Where(w => w.Contains(“a”))
.OderBy(o => o.Length)
.Select(s => s.ToUpper());
~~~
How is it called?

A
var names = {"Tom", "Jim", "Harry"};
IEnumerable<string> filtered = Enumerable.Where(w => w.Contains("a"));

IEnumerable<string> ordered = Enumerable.OderBy(o => o.Length);

IEnumerable<string> upperCased = Enumerable.Select(s => s.ToUpper());

Compiler would use the standart query operators (Enumerable.Where) and apply progressive syntax (each query as new variable)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

How does the insides of a query operator called?

Be specific

A

If we take Enumerable.Where(w => w.Contains("a"); the w => w.Contains("a") part is a Lambda expression. And if Lambda expression takes a value and returns a bool value (as it does here) it is called a Predicate

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Write a query expression in query syntax where items in the given list would be filtered by containing letter, sorted alphabetically and then changed to be upper case

A

Lets say we are given this array:
string [] names = {"Linda", "Ewa", "Arnas", "Simon", "Patrik", "Oscar"};
Our query (in query syntax) would look like this:
~~~
IEnumerable<string> query =
from names n in names
where w.Contains("a")
orderby o.Length
select s.ToUpper();
~~~
Query expressions always start with *from* clause (aka range variable) and end with either a *select* or *group* clause</string>

Result: EWA, ARNAS, LINDA, OSCAR, PATRIK

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Compare LINQ syntax vs. SQL syntax

A
  • Variables: In LINQ you cannot use a variable before you declare it. In SQL, you can reference a table alias in the SELECT clause before defining it in a FROM clause
  • Subqueries: In LINQ is just another C# expression and so requires no special syntax. In SQL subqueries are subject to special rules.
  • Logic flow: With LINQ, data logically flows from left to right through a query. With SQL, the order is less well structured with regards to data flow
  • Query composition: A LINQ comprises a conveyor belt or pipeline of operators that accept and emit sequences whose element order can matter. An SQL query comprises a network of clauses that work mostly with unordered sets.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Can you mix query syntaxes in a query?

A

Yes you can.
If we have this starting array:
string [] names = {"Linda", "Ewa", "Arnas", "Simon", "Patrik", "Oscar"};
we can combine syntaxes to find out how many names in this array contais a letter ‘a’:
int matches = (from n in names where w.Contains("a") select s).Count();
Result : 5

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

What is a deffered or lazy execution in LINQ?

A

In LINQ, “deferred execution” or “lazy execution” refers to the way that a LINQ query is executed. With deferred execution, the query is not executed until it is actually needed to produce a result. The query is stored as a expression tree and is executed only when the query is enumerated, for example, by calling ToList(), First(), Count(), or by iterating through the result using a foreach loop.

The main advantage of deferred execution is that it allows you to perform operations on the query without actually executing it until the results are actually needed. This can lead to improved performance and reduced resource utilization because the query is executed only when it is necessary to produce the result, and not before.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

How the query operators can be specified based on their behaviour to the query?

Give a definition an an example for each

A
  1. Decorators - query operators which modify or decorate the behavior of a query. Examples would be .Where(w => w.Id == "1"). When we apply this instead of full query result we will get only ones that comply with the filter we just wrote. That is how we are modifying the behaviour of query.
  2. Proxies - A query operator can also be referred to as a “proxy” when it acts as a placeholder for the actual data that will be retrieved when the query is executed. For example, the .AsEnumerable() operator can be considered a proxy because it converts a query that is based on a specific data source, such as an IQueryable instance, into a query that is based on an IEnumerable instance.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

What are the three strategies for building more complex queries?

A
  1. Progressive query construction;
  2. Using the into keyword;
  3. Wrapping queries;
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

How can we use anonymous types in our LINQ query?

A

So let’s say we want to save unchanged array to one parameter and changed array to another parameter, without having to create one-off class for only that.
Our starter array:
string[] names = {"Tom", "Jim", "Harry", "Mary", "Jay"};
We then can write this anonymous query like this (note that after select new we don’t specify which specific type we are creating):
~~~
var intermediate = from n in names
select new
{
Original = n,
Vowelless = n.Replace(“a”, “”).Replace(“e”, “”)
};
```

variable “intermediate” needs to be declared with ‘var’ here

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Define differences between ‘into’ keyword and ‘let’ keyword in LINQ queries

A

The into and let keywords are used in LINQ (Language Integrated Query) to modify intermediate results in a query. The main difference between them lies in what they allow you to do in the query.

The let keyword is used to introduce a new range variable and to assign it a value based on the current range variables. For example:
~~~
var result = from x in source
let y = x + 1
select y;

In the above example, the `let` keyword is used to introduce a new range variable y, which is assigned the value of x + 1. This allows you to perform additional operations on the result before returning it.

The `into` keyword is used to continue a query into another query expression. For example:

var finalResult = from x in source
where x > 10
into y
where y < 20
select y;
~~~

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

What architectures do LINQ provide? Define them and describe the differences between them

A

LINQ provides to parallel architectures: local queries for local object collections and interpreted queries for remote data sources
Local queries - operate over collections implementing IEnumerable<T>. Local queries resolve to query operators in the Enumerable class (by default), which in turn solve to chains of decorator sequences. The delegates they accept (expressed in query syntax, fluent syntax or traditional delegates) are fully local to Intermediate Language (IL) code, just like any other C# method.
Interpreted queries - by contrast are descriptive. They operate over sequences that implement IQueryable<T>, and they resolve to the query operators in the Queryable class, which emit expression trees taht are interpreted at runtime. These expression trees can be translated for instance to SQL queries, allowing you to use LINQ to query a database. By using Entity Framework Core (EF Core) framework we are able to reach sequences of type IQueryable<T> which is an extension of IEnumerable<T>

17
Q

If we have a interpreted query, which uses .Where() clause to filter table entities and we know that interpreted queries resolve to query operators in Queryable class, which is a subclass of Enumerable class. Question: to which class would this query resolve its operators? How do those two methods differ?

A

The compiler chooses Queryable.Where because its signature is more specific match.
Queryable.Where accepts a predicate wrapped in an Expression<TDelegate> type. This instructs the compiler to transalte the supplied lambda expression to an expression tree rather than a compiled delegate. An expression tree is an object model based on the types in System.Linq.Expressions that can be inspected at runtime (so that EF Core can later translate it to an SQL statement).

18
Q

What is the difference between interpreted queries and local queries in how they are executed under the hood?

A

With local queries the execution acts like a conveyor belt - the output sequence of one operator is the input sequence of the next. Important feature of most queries is that they execute not when constructed but when enumerated.
However, when you enumerate an IQueryable conveyor belt, it does not start the whole production line, like local queries. Instead, just IQueryable belt starts up, with a special enumerator that calls upon a production manager. Manager reviews method call expressions with instructions written in expression trees. Then manager traverses all the expressions transcribing them to a SQL statement. The SQL statement is executed and results returned to consumer.

19
Q

Explain:

Can we combine local queries and interpreted queries?

A

Yes we can. If we are trying to get data from database, we’re probably using intepreted queries which operators implement IQyaryable<T>. At any time we can call AsEnumerable() method so it can cast an IQueryable<T> sequence to IEnumerable<T>, forcing subsequent query operators to bind to Enumerable operators instead of Queryable operators. This causes the remainder of the query to execute locally.

20
Q

Define:

DbContext

A

Its a class that is subclassed when we want to represent our session working with a database while using EF Core. Typically our subclass of DbContext will contain a one DbSet<T> for each entity in the model:
~~~
public class ExampleContext: DbContext
{
public DbSet<Chapter> Chapters {get; set;}
... properties of other tables ...
}
~~~
A DbContext object does three things:
1. It acts as a factory for generating `DbSet<>` objects that you can query;
2. It keeps track of any changes that you make to your entities so that you can write them back;
3. It provides virtual methods that you can override to configure the connection and model;
Required connection string is usually retrieved from appsettings.json and its pretty standart to each dababase provider</Chapter>

DbContext implements IDisposable so it forces the context’s connections to dispose - but this is usually unnecessary because EF Core closes connections automatically after each query is finished with retrieving results;

21
Q

What is a fluent API in EF Core?

A

A fluent API in Entity Framework Core (EF Core) is a way of defining the structure of your database and the relationships between tables by using a series of method calls that can be chained together to form a fluent syntax. This is in contrast to the traditional “imperative” style of database mapping where you write code to specify the structure of your database, usually using attributes or XML configuration files.
To use the fluent API in EF Core, you typically extend the DbContext class and then use the ModelBuilder object in the OnModelCreating method to configure your entities and relationships. Here’s an example:
~~~
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }</Post></Blog>

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(b => b.Posts)
        .WithOne(p => p.Blog)
        .HasForeignKey(p => p.BlogId);
} } ~~~
22
Q

Let’s say we have created Entity models for our code (code-first approach). How would you exactly proceed with database creation? What operations will be done this way?

A

We would need to use EF Core migrations feature. Migrations would not only create the database, but will configure it in such way that EF Core can automatically update the schema in the future when entity classes change.

To enable migrations and create a database, following commands needs to be called from the NuGet Package Console:
1. Install-Package Microsoft.EntityFrameworkCore.Tools
2. Add-Migration InitialCreate
3. Update-Database

23
Q

What is a object tracking?

A

Context’s ability to distinguish to separate rows that are being added to the same row in the table and preventing that from happening (given the row has a primary key).

24
Q

If we’re working with EF Core, and are writing a query where we want to retrieve the newest serial number from DB, how can we ensure that we will get the newest entry that was just updated and not a cached one?

A

There are couple of ways of doing that
First but not the most widely used is to call Reload() method at the end of a query
Second and most common way is to use a fresh DbContext for each Query we call. using a fresh instance of the DbContext can help ensure that you get the newest entry from the database. The DbContext class in Entity Framework Core (EF Core) is designed to be short-lived and to be used for a single unit of work. This means that you should create a new instance of the DbContext for each query, transaction, or other unit of work that you need to perform.

When you create a new instance of the DbContext, EF Core will establish a new connection to the database and retrieve the latest data from the database. If another process has updated the database since your last query, the new DbContext instance will retrieve the updated data.

25
Q

What is happening behind the hood when we call SaveChanges() after several queries?

A

When the SaveChanges() gets called, EF Core uses the information in the ChangeTracker to construct SQL statements that will update the database to match the changes in our objects, issuing insert statements to add new rows, update statements to modify data and delete statements to remove rows that were removed from the object graph in the DbContext subclass.

26
Q

What happens to a new entity when it is being added to a collection that has a foreign keys, after we press SaveChanges()? What happens when we remove an entity ?

A

When a new entity is added to a collection, EF Core automaticcally populates the foreign keys upon calling SaveChanges().
When the entity is removed from a collection and SaveChanges() is called, EF Core will either clear the foreign key field or delete the corresponding row from the database, depending on how the relationship has been configured or inferred.

27
Q

What is a explicit, eager and lazy loading ?

A

Explicit loading is a feature in Entity Framework Core (EF Core) that allows you to load related entities manually, on demand, instead of relying on lazy loading or eager loading.

In lazy loading, related entities are loaded automatically when they are first accessed. In eager loading, you can specify which related entities should be loaded together with the main entity, but this happens in a single round trip to the database, so you may end up loading more data than necessary.

With explicit loading, you have more control over what related data is loaded and when, which can help you optimize your database queries for performance and avoid loading unnecessary data. You can use the Load method to load related entities explicitly, for example:

dbContext.Entry(customer).Collection(c => c.Purchases).Load();
// customer.Purchases is now populated

28
Q

How can we convert Exptression tree to delegate?

A

By calling Compile() on it.

void Test()
{
  var dbContext = new ExampleContext();
	Product[] localProducts = dbContext.Products.ToArray();
	
	IQueryable<Product> interprettedQuery = dbContext.Products.Where(Product.IsSelling());
	
	IEnumerable<Product> localQuery = localProducts.Where(Product.IsSelling().Compile());
29
Q

What is an expression tree?

A

Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y. The base class for all nodes is the nongeneric Expression class.
Expression trees are used in LINQ (Language Integrated Query) to represent a query as a tree-like structure that can be analyzed and modified before it is executed.
~~~
static void Main(string[] args)
{
// Create an expression tree that represents the expression “2 + 3”
Expression<Func<int>> expression = () => 2 + 3;</int>

        // Compile the expression tree into a delegate and execute it
        Func<int> func = expression.Compile();
        int result = func();

        Console.WriteLine(result); // Output: 5
    } ~~~ In this example, we create an expression tree that represents the expression 2 + 3. We then compile the expression tree into a delegate Func<int> and execute it to get the result 5. The expression tree can be analyzed and modified before it is compiled and executed, making it a powerful tool for LINQ.
30
Q

What distinguishes LambdaExpression from ordinary Expression?

A

LambdaExpression is a subclass of Expression that represents a lambda expression in C#. In other words, it’s a type of expression that represents a method that can be passed as an argument or used as a delegate.

A lambda expression is a shorthand way of writing a delegate or an Expression that can be executed to produce a result. It consists of a set of parameters and an expression that represents the body of the method.

What distinguishes LambdaExpression from Expression is that a LambdaExpression specifically represents a lambda expression, while Expression is a more general type that can represent any type of expression. Additionally, a LambdaExpression has information about the parameters of the lambda expression and the delegate type that the lambda expression represents, whereas a regular Expression does not have this information.
~~~
static void Main(string[] args)
{
// Create a LambdaExpression that represents a lambda expression
LambdaExpression lambdaExpression = Expression.Lambda(
Expression.Add(
Expression.Constant(2),
Expression.Constant(3)
),
Enumerable.Empty<ParameterExpression>()
);</ParameterExpression>

        // Create an Expression that represents an addition expression
        Expression expression = Expression.Add(
            Expression.Constant(2),
            Expression.Constant(3)
        );

        // Compile the LambdaExpression into a delegate and execute it
        Func<int> func = (Func<int>)lambdaExpression.Compile();
        int result = func();
        Console.WriteLine("Result of LambdaExpression: " + result); // Output: 5

        // Convert the Expression into a delegate and execute it
        func = Expression.Lambda<Func<int>>(expression).Compile();
        result = func();
        Console.WriteLine("Result of Expression: " + result); // Output: 5
    } ~~~