.Net Runtime Library for Delphi
Close
How to Use Linq and Dynamic Linq Interfaces

LINQ (Language Integrated Query) is uniform query syntax used to save and retrieve data from different sources. It provides a single querying interface for different types of data sources. For example, SQL is a Structured Query Language used to save and retrieve data from a database. In the same way, LINQ is a structured query syntax used to save and retrieve data from different types of data sources like an Object Collection, SQL server database, XML, web service etc. LINQ always works with objects so you can use the same basic coding patterns to query and transform data in XML documents, SQL databases, ADO.NET Datasets, .NET collections, and any other format for which a LINQ provider is available.
LINQ is nothing but the collection of methods for interfaces with methods AsIEnumerable and AsIQueryable. Linq interfaces can be defined in CNClrLib.Linq namespaces and Dynamic Linq interfaces are defined in CNClrLib.DynamicLinq namespace. LINQ has two main interfaces: Theses are:

  1. Enumerable Interface
  2. Queryable Interface

Enumerable Interface

Enumerable interface includes methods for interfaces that has AsGenericIEnumerable method, this include all the generic collection types such as GenericList interface, GenericDictionary interface, GenericSortedList interface, GenericQueue interface, GenericHashSet interface, GenericLinkedList interface etc. The following demonstrates how to use Enumerable interface methods such as First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, Count, Contains, Sum, Min, Max. The other methods such as Select, where etc which accepts a generic predicate can only be used if the predicate method is defined in a .net assembly. In future version, you will be able to create GenericPredicate interface from a delphi function pointer. In the meantime there is an alternative solution which is to use Dynamic Linq. The next Linq interface (Queryable Interface) explains how to use the dynamic Linq methods.

Delphi
program LinqWithEnumerableInterface; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Collections, CNClrLib.DynamicLinq, CNClrLib.Linq, CNClrLib.Core; var Console: _Console; IntList: _GenericList; intType: _Type; Enumerable: _Enumerable; ListEnumerable: _GenericIEnumerable; I: Integer; begin Console := CoConsole.CreateInstance; //Integer Type intType := TClrAssembly.GetType('System.Int32'); //Integer Collections IntList := CoGenericList.CreateInstance(intType); IntList.Add(33); IntList.Add(45); IntList.Add(50); IntList.Add(45); IntList.Add(34); IntList.Add(23); IntList.Add(67); Console.WriteLine_14('Elements in the List:'); for I := 0 to IntList.Count - 1 do Console.WriteLine_8(IntList[I]); //Get GenericIEnumerable from the GenericList ListEnumerable := IntList.AsGenericIEnumerable; Enumerable := CoEnumerable.CreateInstance; Console.WriteLine_14('Enumerable Interface Methods: '); Console.WriteLine_15('First: {0}', Enumerable.First(intType, ListEnumerable)); Console.WriteLine_15('FirstOrDefault: {0}', Enumerable.FirstOrDefault(intType, ListEnumerable)); Console.WriteLine_15('Last: {0}', Enumerable.Last(intType, ListEnumerable)); Console.WriteLine_15('LastOrDefault: {0}', Enumerable.LastOrDefault(intType, ListEnumerable)); try Console.WriteLine_15('Single: {0}', Enumerable.Single(intType, ListEnumerable)); except on E: Exception do Console.WriteLine_15('Single: {0}', E.Message); end; try Console.WriteLine_15('SingleOrDefault: {0}', Enumerable.SingleOrDefault(intType, ListEnumerable)); except on E: Exception do Console.WriteLine_15('SingleOrDefault: {0}', E.Message); end; Console.WriteLine_15('ElementAt: {0}', Enumerable.ElementAt(intType, ListEnumerable, 4)); Console.WriteLine_15('ElementAtOrDefault: {0}', Enumerable.ElementAtOrDefault(intType, ListEnumerable, 5)); Console.WriteLine_15('Count: {0}', Enumerable.Count(intType, ListEnumerable)); Console.WriteLine_15('Contains: {0}', Enumerable.Contains(intType, ListEnumerable, 30)); Console.WriteLine_15('Contains: {0}', Enumerable.Contains(intType, ListEnumerable, 34)); Console.WriteLine_15('Sum: {0}', Enumerable.Sum(ListEnumerable)); Console.WriteLine_15('Min: {0}', Enumerable.Min(ListEnumerable)); Console.WriteLine_15('Min with Int32 Type:{0}', Enumerable.Min_1(intType, ListEnumerable)); Console.WriteLine_15('Max: {0}', Enumerable.Max(ListEnumerable)); Console.WriteLine_15('Max with Int32 Type:{0}', Enumerable.Max_1(intType, ListEnumerable)); Console.WriteLine_15('Average: {0}', Enumerable.Average(ListEnumerable)); Console.ReadLine(); end. (* This code example produces the following output: Elements in the List: 33 45 50 45 34 23 67 Enumerable Interface Methods: First: 33 FirstOrDefault: 33 Last: 67 LastOrDefault: 67 Single: Sequence contains more than one element SingleOrDefault: Sequence contains more than one element ElementAt: 34 ElementAtOrDefault: 23 Count: 7 Contains: False Contains: True Sum: 297 Min: 23 Min with Int32 Type:23 Max: 67 Max with Int32 Type:67 Average: 42.4285714285714 *)







































































































Queryable Interface

The Queryable interface includes methods for interfaces that has AsGenericIQueryable method. GenericIQueryable interface is used to provide querying capabilities against a specific data source where the type of the data is known. For example, Entity Framework api interfaces has AsGenericIQueryable method to support LINQ queries with underlaying database like SQL Server. The following demonstrates how to use dynamic Queryable interface methods such as First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, Count, Contains, Sum, Min, Max .

Delphi
program LinqWithDynamicQueryableInterface; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Collections, CNClrLib.DynamicLinq, CNClrLib.Linq, CNClrLib.Core; var Console: _Console; IntList: _GenericList; intType: _Type; ListQueryable: _IQueryable; DynamicQry: _ClrDynamicQueryable; I: Integer; begin Console := CoConsole.CreateInstance; //Integer Type intType := TClrAssembly.GetType('System.Int32'); //Integer Collections IntList := CoGenericList.CreateInstance(intType); IntList.Add(33); IntList.Add(45); IntList.Add(50); IntList.Add(45); IntList.Add(34); IntList.Add(23); IntList.Add(67); Console.WriteLine_14('Elements in the List:'); for I := 0 to IntList.Count - 1 do Console.WriteLine_8(IntList[I]); //Create Dynamic Queryable Linq Interface DynamicQry := CoClrDynamicQueryable.CreateInstance; //Get _Queryable Interface from the GenericList ListQueryable := DynamicQry.AsQueryable_6(IntList); Console.WriteLine_14('Enumerable Interface Methods: '); Console.WriteLine_15('First: {0}', DynamicQry.First_1(ListQueryable)); Console.WriteLine_15('FirstOrDefault: {0}', DynamicQry.FirstOrDefault(ListQueryable)); Console.WriteLine_15('Last: {0}', DynamicQry.Last(ListQueryable)); Console.WriteLine_15('LastOrDefault: {0}', DynamicQry.LastOrDefault(ListQueryable)); try Console.WriteLine_15('Single: {0}', DynamicQry.Single(ListQueryable)); except on E: Exception do Console.WriteLine_15('Single: {0}', E.Message); end; try Console.WriteLine_15('SingleOrDefault: {0}', DynamicQry.SingleOrDefault(ListQueryable)); except on E: Exception do Console.WriteLine_15('SingleOrDefault: {0}', E.Message); end; Console.WriteLine_15('ElementAt: {0}', DynamicQry.ElementAt(ListQueryable, 4)); Console.WriteLine_15('ElementAtOrDefault: {0}', DynamicQry.ElementAtOrDefault(ListQueryable, 5)); Console.WriteLine_15('Count: {0}', DynamicQry.Count(ListQueryable)); Console.WriteLine_15('Contains: {0}', DynamicQry.Contains(ListQueryable, 30)); Console.WriteLine_15('Contains: {0}', DynamicQry.Contains(ListQueryable, 34)); Console.WriteLine_15('Sum: {0}', DynamicQry.SumInt32(ListQueryable)); Console.WriteLine_15('Min: {0}', DynamicQry.Min(ListQueryable)); Console.WriteLine_15('Max: {0}', DynamicQry.Max(ListQueryable)); Console.WriteLine_15('Average: {0}', DynamicQry.AverageInt32(ListQueryable)); Console.ReadLine(); end. (* This code example produces the following output: Elements in the List: 33 45 50 45 34 23 67 Enumerable Interface Methods: First: 33 FirstOrDefault: 33 Last: 67 LastOrDefault: 67 Single: Sequence contains more than one element SingleOrDefault: Sequence contains more than one element ElementAt: 34 ElementAtOrDefault: 23 Count: 7 Contains: False Contains: True Sum: 297 Min: 23 Max: 67 Average: 42.4285714285714 *)
























































































Standard Query Operators in Enumerable and Queryable Interfaces

Classification
Standard Query Operators
Filtering
Where, OfType
Sorting
OrderBy, OrderByDescending, ThenBy, ThenByDescending, Reverse
Grouping
GroupBy, ToLookup
Join
GroupJoin, Join
Projection
Select, SelectMany
Aggregation
Aggregate, Average, Count, LongCount, Max, Min, Sum
Quantifiers
All, Any, Contains
Elements
ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault
Set
Distinct, Except, Intersect, Union
Partitioning
Skip, SkipWhile, Take, TakeWhile
Concatenation
Concat
Equality
SequenceEqual
Generation
DefaultEmpty, Empty, Range, Repeat
Conversion
AsEnumerable, AsQueryable, Cast, ToArray, ToDictionary, ToList

Lamdba Expression

The lambda expression is a shorter way of representing anonymous method using some special syntax. It is mostly used to create delegates in LINQ. Simply put, it's a method without a declaration, i.e., access modifier, return value declaration, and name.
How to build Lambda Expression and use in IQueryable Interface
Assuming you have C# code which uses predicates to find value in a generic list.
C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DesignPattern { class Program { static void Main(string[] args) { List<int> Lst = new List<int>(); Lst.Add(100); Lst.Add(200); Lst.Add(300); int Value = Lst.Find(m => m.Equals(100)); Console.WriteLine(Value); Console.ReadLine(); } } } //Output // 100
















To generate the same code in Delphi using the runtime library, you need to use the DynamicExpression to build a lambda expression which will be built into predicate.
Delphi
program ExampleApp; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Collections, CNClrLib.DynamicLinq, CNClrLib.Linq, CNClrLib.Core; var Console: _Console; DynamicQry: _ClrDynamicQueryable; Lst: _GenericList; expParamArray: _ParameterExpressionArray; expParam: _ParameterExpression; expHelper: _ExpressionHelper; DynamicExpr: _ClrDynamicExpression; LambdaExpr: _LambdaExpression; intType, retType: _Type; Value: Integer; exprStr: string; begin Console := CoConsole.CreateInstance; //Integer Type intType := TClrAssembly.GetType('System.Int32'); //Boolean Type retType := TClrAssembly.GetType('System.Boolean'); //Integer Collections Lst := CoGenericList.CreateInstance(intType); Lst.Add(100); Lst.Add(200); Lst.Add(300); //Create Dynamic Queryable Linq Interface DynamicQry := CoClrDynamicQueryable.CreateInstance; //The Lambda Expression as String exprStr := 'm.Equals(100)'; //Create Expression Parameter Array expParamArray := CoParameterExpressionArray.CreateInstance(1); //Create Expression Helper expHelper := CoExpressionHelper.CreateInstance; //Create Parameter Expression and assign to the array index 0; //the m parameter in the expression is of integer type. expParam := expHelper.Parameter_1(intType, 'm'); expParamArray[0] := expParam; //Create Dynamic Lamdba Expression by passing the parameter expression, //the return type of the expression is boolean DynamicExpr := CoClrDynamicExpression.CreateInstance; LambdaExpr := DynamicExpr.ParseLambda_4(expParamArray, retType, exprStr, nil); //Convert the Compiled Lamdba Expression which is Func<int, bool> to Predicate<int> and //find value in list base on the predicate. Value := Lst.Find(TClrGenericHelper.ToGenericPredicate(LambdaExpr.Compile, intType)); Console.WriteLine_8(Value); Console.ReadLine(); end. (* This code example produces the following output: 100 *)











































































The following example uses dynamic labda expression to find all even number from collection.
Delphi
program ExampleApp; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Collections, CNClrLib.DynamicLinq, CNClrLib.Linq, CNClrLib.Core; var Console: _Console; DynamicQry: _ClrDynamicQueryable; ListQueryable: _IQueryable; Lst, evensLst: _GenericList; expParamArray: _ParameterExpressionArray; expParam: _ParameterExpression; expHelper: _ExpressionHelper; ListEnumerator: _IEnumerator; DynamicExpr: _ClrDynamicExpression; LambdaExpr: _LambdaExpression; intType, retType: _Type; exprStr: string; begin Console := CoConsole.CreateInstance; //Integer Type intType := TClrAssembly.GetType('System.Int32'); //Boolean Type retType := TClrAssembly.GetType('System.Boolean'); //Integer Collections Lst := CoGenericList.CreateInstance(intType); Lst.Add(1); Lst.Add(2); Lst.Add(3); Lst.Add(4); Lst.Add(5); Lst.Add(6); Lst.Add(7); Console.WriteLine_14('Elements in the List'); ListEnumerator := Lst.AsIEnumerable.GetEnumerator; while ListEnumerator.MoveNext do Console.WriteLine_12(ListEnumerator.Current); //Create Dynamic Queryable Linq Interface DynamicQry := CoClrDynamicQueryable.CreateInstance; //Get _Queryable Interface from the GenericList ListQueryable := DynamicQry.AsQueryable_6(Lst); //The Lambda Expression as String exprStr := 'n % 2 == 0'; //Create Expression Parameter Array expParamArray := CoParameterExpressionArray.CreateInstance(1); //Create Expression Helper expHelper := CoExpressionHelper.CreateInstance; //Create Parameter Expression and assign to the array index 0; //the m parameter in the expression is of integer type. expParam := expHelper.Parameter_1(intType, 'n'); expParamArray[0] := expParam; //Create Dynamic Lamdba Expression by passing the parameter expression, //the return type of the expression is boolean DynamicExpr := CoClrDynamicExpression.CreateInstance; LambdaExpr := DynamicExpr.ParseLambda_4(expParamArray, retType, exprStr, nil); //Convert the Compiled Lamdba Expression which is Func<int, bool> to Predicate<int> //Findall the match using the predicate delegate evensLst := Lst.FindAll(TClrGenericHelper.ToGenericPredicate(LambdaExpr.Compile, intType)); Console.WriteLine_14('List of Even numbers:'); ListEnumerator := evensLst.AsIEnumerable.GetEnumerator; while ListEnumerator.MoveNext do Console.WriteLine_12(ListEnumerator.Current); Console.ReadLine(); end. (* This code example produces the following output: Elements in the List 1 2 3 4 5 6 7 List of Even numbers: 2 4 6 *)















































































Alternatively, you can use Queryable interface method Where to achieve the same output.
Delphi
program ExampleApp; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Host.Helper, CNClrLib.Collections, CNClrLib.DynamicLinq, CNClrLib.Linq, CNClrLib.Core; var Console: _Console; DynamicQry: _ClrDynamicQueryable; ListQueryable: _IQueryable; Lst: _GenericList; expParamArray: _ParameterExpressionArray; expParam: _ParameterExpression; expHelper: _ExpressionHelper; ListEnumerator: _IEnumerator; DynamicExpr: _ClrDynamicExpression; LambdaExpr: _LambdaExpression; intType, retType: _Type; exprStr: string; begin Console := CoConsole.CreateInstance; //Integer Type intType := TClrAssembly.GetType('System.Int32'); //Boolean Type retType := TClrAssembly.GetType('System.Boolean'); //Integer Collections Lst := CoGenericList.CreateInstance(intType); Lst.Add(1); Lst.Add(2); Lst.Add(3); Lst.Add(4); Lst.Add(5); Lst.Add(6); Lst.Add(7); Console.WriteLine_14('Elements in the List'); ListEnumerator := Lst.AsIEnumerable.GetEnumerator; while ListEnumerator.MoveNext do Console.WriteLine_12(ListEnumerator.Current); //Create Dynamic Queryable Linq Interface DynamicQry := CoClrDynamicQueryable.CreateInstance; //The Lambda Expression as String exprStr := 'n % 2 == 0'; //Create Expression Parameter Array expParamArray := CoParameterExpressionArray.CreateInstance(1); //Create Expression Helper expHelper := CoExpressionHelper.CreateInstance; //Create Parameter Expression and assign to the array index 0; //the m parameter in the expression is of integer type. expParam := expHelper.Parameter_1(intType, 'n'); expParamArray[0] := expParam; //Create Dynamic Lamdba Expression by passing the parameter expression, //the return type of the expression is boolean DynamicExpr := CoClrDynamicExpression.CreateInstance; LambdaExpr := DynamicExpr.ParseLambda_4(expParamArray, retType, exprStr, nil); Console.WriteLine_14('List of Even numbers:'); //Get _Queryable Interface from the GenericList ListQueryable := DynamicQry.AsQueryable_6(Lst); ListEnumerator := DynamicQry.Where_2(ListQueryable, LambdaExpr).AsIEnumerable.GetEnumerator; while ListEnumerator.MoveNext do Console.WriteLine_12(ListEnumerator.Current); Console.ReadLine(); end. (* This code example produces the following output: Elements in the List 1 2 3 4 5 6 7 List of Even numbers: 2 4 6 *)






















































































The following anonymous method checks if student is teenager or not:
//Anonymous method in C#: delegate(Student s) { return s.Age > 12 && s.Age < 20; };
 
 
The above anonymous method can be represented using a Lambda Expression in C#
//Lambda Expression in C#: s => s.Age > 12 && s.Age < 20
 
 
The above expressions can be represented in delphi using Dynamic Lambda Expression.
Delphi
var DynamicQry: _ClrDynamicQueryable; expParamArray: _ParameterExpressionArray; expParam: _ParameterExpression; expHelper: _ExpressionHelper; DynamicExpr: _ClrDynamicExpression; LambdaExpr: _LambdaExpression; intType, retType: _Type; exprStr: string; delegate: _Delegate; begin //Student Type intType := TClrAssembly.GetType('ClassP.Student'); //Boolean Type retType := TClrAssembly.GetType('System.Boolean'); //The Lambda Expression as String exprStr := 's.Age > 12 && s.Age < 20'; //Create Expression Parameter Array expParamArray := CoParameterExpressionArray.CreateInstance(1); //Create Expression Helper expHelper := CoExpressionHelper.CreateInstance; //Create Parameter Expression and assign to the array index 0; //the m parameter in the expression is of integer type. expParam := expHelper.Parameter_1(intType, 's'); expParamArray[0] := expParam; //Create Dynamic Lamdba Expression by passing the parameter expression, //the return type of the expression is boolean DynamicExpr := CoClrDynamicExpression.CreateInstance; LambdaExpr := DynamicExpr.ParseLambda_4(expParamArray, retType, exprStr, nil); delegate := LambdaExpr.Compile; end.




































 
Lambda Expression with Multiple Parameters
Example: Specify parameter type in lambda expression C#:
(Student s,int youngAge) => s.Age >= youngage;
 
This can be represented in delphi as follows using dynamic lamdba expression. The Lambda expression will be compiled to a delegate which will be the same as above c# code:
Delphi
var DynamicQry: _ClrDynamicQueryable; expParamArray: _ParameterExpressionArray; expParam1, expParam2: _ParameterExpression; expHelper: _ExpressionHelper; DynamicExpr: _ClrDynamicExpression; LambdaExpr: _LambdaExpression; intType1, intType2, retType: _Type; exprStr: string; delegate: _Delegate; begin //Student Type intType1 := TClrAssembly.GetType('ClassP.Student'); //Integer Type intType2 := TClrAssembly.GetType('System.Int32'); //Boolean Type retType := TClrAssembly.GetType('System.Boolean'); //The Lambda Expression as String exprStr := 's.Age >= youngage'; //Create Expression Parameter Array expParamArray := CoParameterExpressionArray.CreateInstance(2); //Create Expression Helper expHelper := CoExpressionHelper.CreateInstance; //Create Parameter Expression and assign to the array index 0; //the m parameter in the expression is of integer type. expParam1 := expHelper.Parameter_1(intType1, 's'); expParam2 := expHelper.Parameter_1(intType2, 'youngage'); expParamArray[0] := expParam1; expParamArray[1] := expParam2; //Create Dynamic Lamdba Expression by passing the parameter expression, //the return type of the expression is boolean DynamicExpr := CoClrDynamicExpression.CreateInstance; LambdaExpr := DynamicExpr.ParseLambda_4(expParamArray, retType, exprStr, nil); delegate := LambdaExpr.Compile; end.