In most applications, we query data from a source, a database, for instance, and perform operations on that data. For this, we use can use LINQ. However, when we want to unlock more powerful features of the C# language, we use the System.Linq.Dynamic.Core library.

In this article, we are going to learn about this library and its features through different use cases.

To download the source code for this article, you can visit our GitHub repository.

Let’s dive in.

What is System.Linq.Dynamic.Core Library?

System.Linq.Dynamic.Core or Dynamic LINQ is an open-source query library that extends the functionalities of LINQ by making the queries dynamic. It’s not included in .NET by default and we install it as a NuGet package in our applications.

This library enables us to use dynamic query strings. To use this library, you should at least have basic knowledge of LINQ. However, if you are not familiar with LINQ or if you feel the need to brush up on your existing LINQ knowledge before proceeding, you can read our article about Basic LINQ Concepts

Now, let’s take a look at a few examples of how we can use this library by implementing a simple application.

Setting up the Application

Using a simple console application, we are going to demonstrate how to use the System.Linq.Dyamic.Core library.

First off, we create a Console application via CLI or Visual Studio. 

Right after creating the project, we need to install the library as a dependency. To install the library, we open up the Package Manager Console window and run the command:

Install-Package System.Linq.Dynamic.Core

With the setup done, we’re ready to explore the features of this library.

Writing Dynamic Queries

To start off, we create three classes.

The first class is the Employee class which describes an employee of a company:

public class Employee
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Department { get; set; }
    public int Age { get; set; }
    public string Residence { get; set; }
    public Employer Employer { get; set; }
}

The second class is the Employer class, with two properties of an employer:

public class Employer
{
    public long Id { get; set; }
    public string Name { get; set; }
}

The third class is EmployeeData class that has one public Employees property and a constructor. Also, the class has one method GetEmployees() that returns employee test data:

public class EmployeeData
{
    public IQueryable<Employee> Employees;

    public EmployeeData()
    {
        GetEmployees();
    }

    public IQueryable<Employee> GetEmployees()
    {
        Employees = new[]
        {
            new Employee
            {
                Id = Guid.NewGuid(),
                Name = "John Doe",
                Department = "IT",
                Age = 25,
                Residence = "Malibu"
            },
            ...
            new Employee
            {
                Id = Guid.NewGuid(),
                Name = "Bob Mkenya",
                Department = "Infrastructure",
                Age = 25,
                Residence = "Kenya",
                Employer = new Employer
                {
                    Id = 0,
                    Name = "Test Company"
                }
            }
        }.AsQueryable();

        return Employees;
    }
}

The return type of the GetEmployees() method is IQueryable<Employee> because we want to simulate a data source, like a database, used in most applications. Also, we shortened the snippet here, but if you want to inspect the entire class, you can do that by checking the class in the source code.

Filtering Data Using Dynamic LINQ

Let’s start with a strongly typed LINQ example to see how that’s done the usual way, and then introduce the dynamic LINQ ways:

public List<Employee> FilterEmployeesByDepartmentUsingTypedLinq(string departmentName) =>
    Employees
        .Where(e => e.Department == departmentName)
        .ToList();

Using dynamic LINQ:

public List<Employee> FilterEmployeesByDepartment(string departmentName) =>
    Employees
        .Where("Department == @0", departmentName)
        .ToList();

First, pass departmentName as a parameter to the FilterEmployeesByDepartment() method. After that, we filter the returned list based on whether employees are in the IT department or not. When filtering, the library gives us the flexibility of using strings instead of strongly typed arguments. Then, we chain ToList() which returns the results as a list of Employee type.

We can also use the string escape sequence since “IT” is also a string:

public List<Employee> FilterEmployeesUsingEscapeSequence() =>
    Employees
        .Where("Department == \"IT\"")
        .ToList();

And the third way would be to use the lambda => operator to filter our results:

public List<Employee> FilterEmployeesUsingLambdaOperator() =>
    Employees
        .Where("c => c.Department = \"IT\"")
        .ToList();

Selecting Data Using Dynamic LINQ

The data we get after querying a data source may contain additional data which we may not need. In order to ensure we only return the relevant data, we use the Select() method. 

Similar to filtering data, we can select data using strongly typed LINQ:

public List<Employee> SelectEmployeesUsingTypedLinq() =>
    Employees
        .Select(e => new Employee
        {
            Name = e.Name,
            Department = e.Department,
        })
        .ToList();

Using dynamic LINQ:

public List<dynamic> SelectEmployees() =>
    Employees
        .Select("new {Name, Department}")
        .ToDynamicList();

Out of all the data, we are only interested in the Name and Department fields. Also, note that the return type for this method is List<dynamic>, not a strongly typed list. When we print the results of this query, we get:

Name                Department
===============================================
John Doe            IT
Jane Doe            Finance
Quincy Nigellus     Finance
Sharron Zachary     IT
Kristia Cletis      Human Resource
Chrissie Elihu      Human Resource
Businge Missy       Hospitality

The results only have two columns, which we specified in the query. We can apply the same to large data sets where we only want to use a subset of that data.

Sorting Data With Dynamic LINQ

We might want to sort the data based on one or multiple properties.

First, let’s start with a strongly typed LINQ:

public List<Employee> SortEmployeesUsingTypedLinq() =>
    Employees
        .OrderBy(x => x.Name)
        .ToList();

Writing the same query using dynamic LINQ:

public List<Employee> SortEmployees() =>
    Employees
        .OrderBy("Name")
        .ToList()

Here, we are sorting the employees using the Name property. In this case, we sort employees in ascending order, which is the default. 

We can also sort the data using more than one property. Using strongly typed LINQ, our query would be:

public List<Employee> SortEmployeesByMultiplePropertiesUsingTypedLinq() =>
    Employees
        .OrderBy(x => x.Department)
        .ThenBy(x => x.Name)
        .ToList();

In dynamic LINQ, we provide a comma-separated list of properties instead:

public List<Employee> SortEmployeesByMultipleProperties() =>
    Employees
        .OrderBy("Department desc, Name")
        .ToList();

We are sorting the employees, first by the Department property in descending manner, then the Name property in ascending order.

When we print out the results and inspect them, we see the sorted data:

Name: Quincy Nigellus, Dept: Finance
Name: Jane Doe, Dept: Finance
Name: Businge Missy, Dept: Hospitality
Name: Kristia Cletis, Dept: Human Resource
Name: Chrissie Elihu, Dept: Human Resource
Name: Sharron Zachary, Dept: IT
Name: John Doe, Dept: IT

On inspecting the results closely, our query sorts the results in ascending order, based on the department.

Advanced Features of the Library

The Dynamic LINQ library offers some advanced features like configuration, null propagation, and dynamic query creation.

Library Configuration

We can pass additional configurations to extend the functionalities of the queries we write. This way, we can dictate the behavior of our applications:

var config = new ParsingConfig()
{
    DateTimeIsParsedAsUTC = true
};

var query = employees
    .Where(config, "Age > 26")
    .ToList();

We first create a configuration by creating a new ParsingConfig object and instantiating the DateTimeIsParsedAsUTC property. In this case, we would like to have the DateTime to be in UTC format. To use this configuration, we pass it as an argument of the dynamic query.

The use case for this would be when retrieving data from the database. We can apply this same approach to all the other types of queries supported by the library.

Null Propagation

There are times when some fields in our data are null. In such cases, the library provides us with two options.

The first option is to return fields that are not null:

public List<Employee> GetEmployeesByEmployer(string employer) =>
    Employees
        .Where("np(Employer.Name) == @0", employer)
        .ToList();

In this example, we use the np() method provided in the library. We fetch all the employees where the employer name is not null and the name is equal to the employer parameter passed in the method signature. 

The second option assigns an alternative value to the Employer property where it is null:

public List<dynamic> GetEmployeesWhereEmployerIsNull() =>
    Employees
        .Select("np(Employer.Name, \"Not Specified\")")
        .ToDynamicList();

In this case, all the employees without an employer specified will have the value “Not Specified”.

Dynamically Creating Lambda Expressions

The library also provides a way to create lambda expressions:

Expression<Func<Employee, bool>> itExpression = DynamicExpressionParser
    .ParseLambda<Employee, bool>(new ParsingConfig(), true, "Department = @0", department);

Expression<Func<Employee, bool>> ageExpression = DynamicExpressionParser
    .ParseLambda<Employee, bool>(new ParsingConfig(), true, "Age >= @0", age);

return Employees
    .Where("@0(it) and @1(it)", itExpression, ageExpression)
    .ToList();

We create two lambda expressions: itExpression and ageExpression.

The first lambda expression returns true if there are employees where the department is IT. Similarly, the second lambda expression returns true if there are employees whose age is greater than 20. Combining the results of the two lambda expressions, we create a query to fetch data and filter it.

Printing the results of the query, we get:

Name: John Doe, Age: 25
Name: Sharron Zachary, Age: 23

In our data, we only have two employees in the IT department whose age is greater than 20.

Conclusion

In this article, we have covered the features of the System.Linq.Dynamic.Core library, from basics to some advanced features. Applying this newly acquired knowledge in implementing applications will greatly improve the quality of code. From here, we highly encourage you to go far and beyond to create what your mind can conceive.