Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to negate a Where clause of an IQueryable

I created an extension method to encapsule some where logic like this (this is a very simplified version):

public static IQueryable<Cargo> ReadyToCarry(this IQueryable<Cargo> q)
{
    VehicleType[] dontNeedCouple = new VehicleType[] { VehicleType.Sprinter, VehicleType.Van, VehicleType.Truck };

    return q.Where(c => c.DriverId > 0 && c.VehicleId > 0)
            .Where(c => c.CoupleId > 0 || dontNeedCouple.Contains(c.Vehicle.Type));
}

So I can use it like this:

using (var ctx = new MyNiceContext())
{
    var readyCargo = ctx.Cargos.ReadyToCarry().OrderBy(c => c.Id).ToList();
    // ...more code
}

Which works nicely, this is translated to SQL and executed by Entity Framework. Now, I have another place I need cargos which are not ready to carry, which means I need exactly the opposite.

My idea was something like this:

public static IQueryable<Cargo> NotReadyToCarry(this IQueryable<Cargo> q)
{
    return !q.ReadyToCarry(); // ofc this doesn't work...
}

using (var ctx = new MyNiceContext())
{
    var readyCargo = ctx.Cargos.NotReadyToCarry().OrderBy(c => c.Id).ToList();
    // OR maybe
    var readyCargo = ctx.Cargos.ReadyToCarry(false).OrderBy(c => c.Id).ToList(); // somehow use that bool param to reverse the logic when false
}

I didn't want to recreate the reverse logic from scratch, so if I needed to change it one day, I'd change in one unique place.

I'm accepting alternatives to this approach, since it's a new project.

like image 832
Alisson Reinaldo Silva Avatar asked Feb 21 '17 13:02

Alisson Reinaldo Silva


People also ask

What inherits from IQueryable?

The IQueryable interface inherits the IEnumerable interface so that if it represents a query, the results of that query can be enumerated. Enumeration causes the expression tree associated with an IQueryable object to be executed.

What is the difference between returning IQueryable T vs IEnumerable T >?

The major difference between IQueryable and IEnumerable is that IQueryable executes query with filters whereas IEnumerable executes the query first and then it filters the data based on conditions.

What is IQueryable return?

IQueryable is executed. // // Returns: // A System.Type that represents the type of the element(s) that are returned when. // the expression tree associated with this object is executed.

Why we use IQueryable in LINQ?

IQueryable is suitable for querying data from out-memory (like remote database, service) collections. While querying data from a database, IQueryable executes a "select query" on server-side with all filters. IQueryable is beneficial for LINQ to SQL queries.


1 Answers

You can use Except() method:

var readyCargo = ctx.Cargos.ReadyToCarry().OrderBy(c => c.Id);
var notReadyCargo = ctx.Cargos.Except(readyCargo);

OR

you can add some parameter to ReadyToCarry():

public static IQueryable<Cargo> ReadyToCarry(this IQueryable<Cargo> q, bool ready = true)
{
    VehicleType[] dontNeedCouple = new VehicleType[] { VehicleType.Sprinter, VehicleType.Van, VehicleType.Truck };

    if (ready)
    {
        return q.Where(c => c.DriverId > 0 && c.VehicleId > 0)
                .Where(c => c.CoupleId > 0 || dontNeedCouple.Contains(c.Vehicle.Type));
    }
    else
    {
        // logic to get not ready for carrying
    }
}

OR

you can combine these two options:

public static IQueryable<Cargo> ReadyToCarry(this IQueryable<Cargo> q, bool ready = true)
{
    VehicleType[] dontNeedCouple = new VehicleType[] { VehicleType.Sprinter, VehicleType.Van, VehicleType.Truck };

    var readyToCarry = q.Where(c => c.DriverId > 0 && c.VehicleId > 0)
                        .Where(c => c.CoupleId > 0 || dontNeedCouple.Contains(c.Vehicle.Type));

    if (ready)
    {
        return readyToCarry;
    }
    else
    {
        return q.Except(readyToCarry);
    }
}

In the last case when you change logic to get ready to carry entities you don't need to change negation of that condition. You should change only one query.

like image 76
Roman Doskoch Avatar answered Sep 19 '22 21:09

Roman Doskoch