Home > Blockchain >  LINQ Include with a Group By and new Object
LINQ Include with a Group By and new Object

Time:03-07

Given the below query

var query = from vehicle in _context.Vehicle.Where(x => x.ClientId == clientId && x.IsActive)

        //Left Outer to BookingVehicle. Used for subsequent Left Outers to Bookings
        join bookingVehicle in _context.BookingVehicle
            on vehicle.Id equals bookingVehicle.VehicleId
            into vehicle_bookingVehicle
        from bv in vehicle_bookingVehicle.DefaultIfEmpty()

        //Left Outer to Booking, for specific statuses. To get count of work-in-progress bookings
        join bookingActive in _context.Booking.Where(x => new[] { 1, 3, 4, 6, 7, 11, 13 }.Contains(x.StatusId))
            on bv.BookingId equals bookingActive.Id
            into bookingVehicle_bookingActive
        from ba in bookingVehicle_bookingActive.DefaultIfEmpty()

        //Left Outer to Booking. To get count of all bookings (irrespective of status), and date of latest booking
        join bookingHistory in _context.Booking
            on bv.BookingId equals bookingHistory.Id
            into bookingVehicle_bookingHistory
        from bh in bookingVehicle_bookingHistory.DefaultIfEmpty()

        group new { vehicle, ba, bh }
        by new { 
            Vehicle = vehicle
        }
        into grp

        select new VehicleItemListDTO
        {
            Vehicle = grp.Key.Vehicle,

            HasBookings = (grp.Count(g => g.ba.Id != null) > 0),
            HasHadBookings = (grp.Count(g => g.bh.Id != null) > 0),
            LastBooking = (grp.Max(g => g.bh.ScheduleStartUtc) != null) ? grp.Max(g => g.bh.ScheduleStartUtc) : grp.Max(g => g.bh.ActualStartUtc),
        };


var results = query.OrderBy(x => x.Vehicle.Id).ToList();

return results;

I have a custom object, VehicleItemListDTO, that includes a Domain Object of Vehicle, and some other parameters on how that vehicle is being used.

This is working fine. However, I would like to include additional data about the Vehicle. Some Vehicles have an assigned VehicleModel.

If I use an Include(y => y.VehicleModel) on the initial FROM statement, then the VehicleModel is not included in the data returned to the caller. (With SQL Profiler, you can see the Left Join, but it is in a nested query, and subsequently lost).

I also tried:

var results = query.Include(z => z.Vehicle.VehicleMake).AsEnumerable().OrderBy(x => x.Id).ToList();

But that then errors at runtime with:

The result type of the query is neither an EntityType nor a CollectionType with an entity element type. An Include path can only be specified for a query with one of these result types.

How can I get my Vehicle's VehicleModel Included to the caller?

CodePudding user response:

try to use include before the query

var query = from vehicle in _context.Vehicle.Include(z => z.Vehicle.VehicleMake).AsEnumerable().OrderBy(x => x.Id).Where(x => x.ClientId == clientId && x.IsActive)

...

CodePudding user response:

Try the following query. I have changed grouping because it is not natural for the SQL to group by whole record

var groupingQuery = 
    from vehicle in _context.Vehicle.Where(x => x.ClientId == clientId && x.IsActive)

    //Left Outer to BookingVehicle. Used for subsequent Left Outers to Bookings
    join bookingVehicle in _context.BookingVehicle
        on vehicle.Id equals bookingVehicle.VehicleId
        into vehicle_bookingVehicle
    from bv in vehicle_bookingVehicle.DefaultIfEmpty()

    //Left Outer to Booking, for specific statuses. To get count of work-in-progress bookings
    join bookingActive in _context.Booking.Where(x => new[] { 1, 3, 4, 6, 7, 11, 13 }.Contains(x.StatusId))
        on bv.BookingId equals bookingActive.Id
        into bookingVehicle_bookingActive
    from ba in bookingVehicle_bookingActive.DefaultIfEmpty()

    //Left Outer to Booking. To get count of all bookings (irrespective of status), and date of latest booking
    join bookingHistory in _context.Booking
        on bv.BookingId equals bookingHistory.Id
        into bookingVehicle_bookingHistory
    from bh in bookingVehicle_bookingHistory.DefaultIfEmpty()

    group new { ba, bh } by new { VehicleId = vehicle.Id } into grp

    select new 
    {
        VehicleId = grp.Key.VehicleId,

        HasBookings = (grp.Count(g => g.ba.Id != null) > 0),
        HasHadBookings = (grp.Count(g => g.bh.Id != null) > 0),
        LastBooking = grp.Max(g => g.bh.ScheduleStartUtc) ?? grp.Max(g => g.bh.ActualStartUtc),
    };

var query = 
    from g in groupingQuery
    join vehicle in _context.Vehicle on g.VehicleId equals vehicle.Id
    select new VehicleItemListDTO
    {
        Vehicle = new Vehicle
        {
            Id = vehicle.Id,
            ... // other fields
            VehicleMake = vehicle.VehicleMake
        },

        HasBookings = g.HasBookings,
        HasHadBookings = g.HasHadBookings,
        LastBooking = g.LastBooking,
    };

var results = query.OrderBy(x => x.Vehicle.Id).ToList();

return results;

We have to construct Vehicle object again to make VehicleMake initialized.

  • Related