Skip to content

Commit

Permalink
ExDM Add documentation to the selected parts of the code #369
Browse files Browse the repository at this point in the history
- Working on StructuralData
  • Loading branch information
mpostol committed May 5, 2024
1 parent 667810f commit de8753c
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 130 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@

- [LINQ to Object](#linq-to-object)
- [Introduction](#introduction)
- [Query Syntax](#query-syntax)
- [Method Syntax](#method-syntax)
- [Deferred Execution of the LINQ Expression](#deferred-execution-of-the-linq-expression)
- [Anonymous type](#anonymous-type)
- [Local Structural Data](#local-structural-data)
- [DataSet to create structural data](#dataset-to-create-structural-data)
Expand All @@ -27,96 +24,6 @@ In this chapter, we will continue to discuss topics related to structured data a

The main goal of embedding the LINQ expressions into the programming language is to create a construct that allows automatic preparation of queries in a domain-specific language compliant with a remote database management system without leaving a comfort zone, I mean to change the programming language. It allows prefetching data from external repositories (for example relational databases) using the same programming environment. But we also noticed that pre-selection makes sense in the case of local data structures, i.e. certain object graphs. Here we will encounter a challenge of how LINQ can help.

## Query Syntax

So let's recall the form of a LINQ expression written using query syntax. An example of the LINQ expression written using query syntax can be found in the method [QuerySyntax][QuerySyntax]:

``` CSharp
public static string QuerySyntax()
{
string[] _words = { "apple", "strawberry", "grape", "peach", "banana" };
IEnumerable<string> _wordQuery = from word in _words
where word[0] == 'g'
select word;
return string.Join(";", _wordQuery.ToArray());
}
```

By definition, an expression is a sequence of operations and operands. Expressions are composed of variables, operators, function calls, and literals. They represent computations or transformations and yield a result. The previously discussed expression written using query syntax does not look like such a sequence at first glance. Unfortunately, the text in the example code contains unknown keywords, such as `from`, `where`, and `select`, but it does not resemble a sequence of operations. To be executed it must be converted to a form compliant with this definition. Okay, but what if not? What are the consequences? In such a case, since we will not be able to recognize this entry as an expression, we will not be able to apply the semantics of the expression to it, i.e. knowledge about how it is implemented, in other words, what this entry means.

Instead of adding a new theory to the right side of the assignment instruction, it seems that it will be simpler to try to convert this syntax to the well-known syntax of an expression, which is a sequence of operators and their operands. It should be noted that conversion - or translation - from one syntax to another means acting on the program text. This, in turn, means that any developer can perform this conversion.

The extension method concept will be helpful and even - one might say - irreplaceable to convert the syntax and obtain the typical syntax of an expression. We learned this concept when examining the basics of functional programming.

## Method Syntax

But now coming to the point, let's start with an observation that as required by a LINQ expression, the data source, in our example, the value of the expression following the `in` keyword must implement the `IEnumerable` interface. If so, let's use the extension method concept to extend this interface.

Using this hint, it's time to look for a solution that will allow us to convert the expression syntax written in the form of a query into an expression written as a sequence of operations and their operands using the mentioned extension methods, but also lambda expressions and anonymous types. We have already learned about all these constructs. Their importance is fundamental, so if there is any doubt about their full understanding, I suggest returning to these topics before continuing to learn the details of the LINQ expression.

An example text after conversion can be found in the [MethodSyntax][MethodSyntax] method,

``` CSharp

public static string MethodSyntax()
{
string[] _words = { "apple", "strawberry", "grape", "peach", "banana" };
IEnumerable<string> _wordQuery = _words.Where<string>(word => word[0] == 'g').Select<String, String>(word => word);
return String.Join(";", _wordQuery.ToArray());
}
```

which can be found in the [LinqMethodSyntaxExamples][LinqMethodSyntaxExamples] class. This method is tested in a separate unit test [MethodSyntaxTest][MethodSyntaxTest]

``` CHarp
public void MethodSyntaxTest()
{
Assert.AreEqual<string>("grape", LinqMethodSyntaxExamples.MethodSyntax());
}
```

The test result and the content of the method indicate that it is functionally equivalent to the previously discussed method.

In the example discussed, the first extension method used in the conversion process is the 'where' method. Of course, the similarity of the names of the keyword `where` in the query syntax and the name of the method in the transformation of this text to a sequence of operations and their operands is not accidental. In other words, the method name is equivalent to the `where` keyword in the query syntax.

Using the `go to definition` menu entry or the F12 key, we will move to the definition of the `Where` method. After scrolling you can see that it is in the static `Enumerable` class of the .NET library.

![Where Method](../.Media/WhereMethod.gif)

This class also contains all other methods that are necessary to perform the described conversion operation. This is an important statement and is worth remembering to understand the answer to the previously asked questions about what the difference is between the LINQ and regular expressions. As I mentioned, this question has been asked before, but we'll come back to it again.

The next keyword in the query syntax is `select`. Again, we can use the extension method of the `IEnumerable` interface because the where method returns an object implementing this interface. As we can see from the definition of the Enumerable class, most of the extension methods located in the Enumerable class are functions that return objects implementing the mentioned interface.

Let's go back to the text of the method in question, which contains a `LINQ` expression after conversion to a string of operations.

Now that we know how to convert a `LINQ` query into a sequence of method calls, let's consider what to do with the constructs following the where and select keywords. At first glance, they look like expressions, and in fact, their syntax follows the syntax of an expression. However, the where method is called on a sequence of elements, so this expression must be executed on every element in the data source. In this case, we check whether the first letter of each word in the array is `g`. To make this possible, following the semantics of the language, we must replace them with a method that will have one parameter with a type consistent with the element type in the source and will return a true or false value. Since we cannot send another method as an argument to a method, let's use the concept of delegate, i.e. references to a method. Additionally, we can write this as an anonymous function using lambda expression syntax.

We can do a similar thing with the expression following the word 'select' and replace it with a delegate to a method using the lambda expression syntax. In our example, this method returns the value of the current argument, so it does nothing and is therefore removed from the next example.

Now let's look at the method text resulting from the query transformation in the context of a unit test. The test shows that the result is identical to the method using the LINQ query syntax.

``` CSharp
Assert.AreEqual<string>("grape", LinqMethodSyntaxExamples.MethodSyntax());
```

## Deferred Execution of the LINQ Expression

So let's check whether, after converting the LINQ expression from query syntax to method syntax, it still retains features indicating that, as before, the expression execution is deferred to make room for possible further translation into a form compliant with the language queries valid for the selected repository, for example, a relational database.

To check this, similarly as before, we modify the data source between the statement to assign the value obtained from the expression after conversion to the _wordQuery variable and the statement to check the final result in the unit test. Since the result we expect is a list of words starting with g, and we receive an empty text, we can state that the operation of determining the value is deferred similarly to before, while in the line

``` CSharp
IEnumerable<string> _wordQuery = _words.Where<string>(word => word[0] == 'g');
```

the `_wordQuery` variable is assigned a value representing only the description of the expression located on the right-hand side of the assignment symbol. In other words, the syntax of a LINQ expression does not matter to its features. In other words, writing an expression using query syntax or method syntax has no impact on the semantics of this notation. In both cases, we deal with LINQ expression regardless of the syntax applied. Since the behavior of LINQ expressions differs significantly from the behavior of typical expressions we have to return to the question of how to distinguish them.

Earlier we said that conversion from query syntax to the method syntax requires the use of extension methods defined in the `Enumerable` class. As we know, for each type definition we can create your extension methods, and this is also true for the `IEnumerable` interface. It is therefore possible to use these methods during the operations discussed. This is of course possible, but the expression will no longer be a LINQ expression and, as a result, cannot be converted to a query that can be executed remotely in an external repository as an equivalent operation. The use of only methods belonging to the `Enumerable` class in an expression is the first distinguishing feature of the LINQ expression, but not the only one.

The code snippets in [LinqMethodSyntaxExamples][LinqMethodSyntaxExamples] demonstrate the process of converting the query syntax of a LINQ expression to the method syntax using lambda expressions as the operands for these operations. Here it is worth asking whether delegates to named custom methods can be used instead of anonymous methods. The answer is yes, but in this case, you need to be aware that such methods may not be known in the external repository making you unable reference to them in a query written in a language dedicated to this repository. This is another criterion for distinguishing LINQ and regular expressions.

## Anonymous type

To describe the next conditions having an impact on the classification of the LINQ expression the next test method must be analyzed and the related library method [AnonymousType][AnonymousType].
Expand Down Expand Up @@ -210,12 +117,8 @@ The last test [FilterPersonsByLastName_MethodSyntaxTest][FilterPersonsByLastName
- [CDCatalog][CDCatalog]
- [TestDataGenerator][TestDataGenerator]

[QuerySyntax]: LINQQueryAndMethodsSyntax/LinqQuerySyntaxExamples.cs#L30-L37
[MethodSyntax]: LINQQueryAndMethodsSyntax/LinqMethodSyntaxExamples.cs#L21-L26
[LinqMethodSyntaxExamples]: LINQQueryAndMethodsSyntax/LinqMethodSyntaxExamples.cs#L19-L47
[AnonymousType]: LINQQueryAndMethodsSyntax/LinqMethodSyntaxExamples.cs#L37-L46

[MethodSyntaxTest]: ../StructuralDataUnitTest/LinqMethodSyntaxExamplesUnitTest.cs#L21-L24
[TestDataGenerator]: ../StructuralDataUnitTest/Instrumentation/TestDataGenerator.cs#L17-L73
[Person]: ../StructuralDataUnitTest/Instrumentation/TestDataGenerator.cs#L29-L47
[CDCatalog]: ../StructuralDataUnitTest/Instrumentation/TestDataGenerator.cs#L61-L72
Expand Down
Loading

0 comments on commit de8753c

Please sign in to comment.