As we discussed last time, LINQ is a technology that integrates querying capabilities directly into the C# language. As you might imagine, that means LINQ queries can be returned from C# Methods.

Now, why would that be important? Well, it’s important because a particular query can be composed through multiple methods. What that means is – you can prepare a query in a base class, for example, and then modify it as you desire.

Let’s see a simple example. First, we will need some data. Let us create a class to represent a Person:

public class Person
{
	public Person(string name, string email, int status)
	{
		Name = name;
		Email = email;
		Status = status;
	}

	public string Name { get; }
        public string Email { get; }
	public int Status { get; }
}

We will want to have some data:

using linq_play.model;

internal class Program
{
    private Person[] persons = new[]
    {
        new Person("Noel Pope", "a@google.net", 3),
        new Person("Justina Merritt", "nec.malesuada@icloud.edu", 1),
        new Person("Justina Florrick", "florrick@icloud.edu", 3),
        new Person("Justin Timberlake", "jtimberla.ke@icloud.edu", 1),
        new Person("Gabriel Dawson", "risus.quis.diam@icloud.com", 1),
        new Person("Caldwell Kirkland", "risus.donec@yahoo.com", 4),
        new Person("Tatum Pope", "at.iaculis@yahoo.org", 2),
    };

    public Program()
    {
    }

    private static void Main(string[] args)
    {
    }
}

Now we can make a simple Method that returns a query which can then be modified further later through various method calls. This might prove useful to you when you wish to prepare a query in a base class, just to ensure that classes that inherit do not forget a crucial filter, like showing only active Users, for example:

private IEnumerable<Person> GetQuery() =>
        from person in persons
        where person.Status == 1
        select person;

Then, the following methods can continue to build on this base method:

private IEnumerable<Person> GetPersonsWhereNameContains(string search)
{
    var query = GetQuery();

    return from p in query
            where
            !string.IsNullOrWhiteSpace(p.Name)
            &&
            p.Name.Contains(search, StringComparison.OrdinalIgnoreCase)
            select p;
}

That means that the users of our class might only use a “final, public method”, while the details of how query is built through various methods might remain hidden. So, the entire example could potentially look like this:

using linq_play.model;

internal class Program
{
    private Person[] persons = new[]
    {
        new Person("Noel Pope", "a@google.net", 3),
        new Person("Justina Merritt", "nec.malesuada@icloud.edu", 1),
        new Person("Justina Florrick", "florrick@icloud.edu", 3),
        new Person("Justin Timberlake", "jtimberla.ke@icloud.edu", 1),
        new Person("Gabriel Dawson", "risus.quis.diam@icloud.com", 1),
        new Person("Caldwell Kirkland", "risus.donec@yahoo.com", 4),
        new Person("Tatum Pope", "at.iaculis@yahoo.org", 2),
    };

    public Program()
    {
    }

    private static void Main(string[] args)
    {
        new Program().Run();
    }

    private IEnumerable<Person> GetQuery() =>
            from person in persons
            where person.Status == 1
            select person;

    private IEnumerable<Person> GetPersonsWhereNameContains(string search)
    {
        var query = GetQuery();

        return from p in query
                where
                !string.IsNullOrWhiteSpace(p.Name)
                &&
                p.Name.Contains(search, StringComparison.OrdinalIgnoreCase)
                select p;
    }

    private void Run()
    {
        var query = GetPersonsWhereNameContains("justina");

        foreach (var person in query)
        {
            Console.WriteLine($"{person.Name} ({person.Email})");
        }

        // Outputs: Justina Merritt (nec.malesuada@icloud.edu)
        Console.ReadKey();
    }
}

So, as you can see above, we got only one “Justina”, even if there were two in our data. That is because we prepared the base query in the GetQuery() method. We then further filtered by name – but in the second method we simply expanded on the first query, which in the end gave us a “combined query”.

You might want to debug the code to see what really happens – as mentioned in the first LINQ Post – the query is only executed and results are only materialized in the foreach call.

As you might imagine, this philosophy will be very handy when using LINQ to write queries that are used to access data in the database – and yes, IEnumerable is wrong in the EF context, but we will yet come to IQueryable, EF and databases. Patience, Luke.