Запрос Linq2Entities для поиска записей с заданным диапазоном Mix-Max

У меня есть эти две сущности:

public class Ticket
{
    public int Id { get; set; }
    public int ScheduleId { get; set; }
    public int SeatId { get; set; }

    [DataType(DataType.Date)]
    [Column(TypeName = "Date")]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime ForDate { get; set; }

    public Seat Seat { get; set; }
    public Schedule Schedule { get; set; }
    public ICollection<TicketStop> TicketStops { get; set; }
}

public class TicketStop
{
    public int Id { get; set; }
    public int TicketId { get; set; }
    public int LineStopStationId { get; set; }

    public Ticket Ticket { get; set; }
    public LineStopStation LineStopStation { get; set; }
}

public class LineStopStation
{
    public int Id { get; set; }
    public int LineId { get; set; }
    public int StopId { get; set; }
    public int Order { get; set; }
    public bool IsLastStop { get; set; }

    public Line Line { get; set; }
    public Stop Stop { get; set; }
}

Бизнес-кейс заключается в том, что я внедряю систему бронирования билетов на автобусы (в основном для целей обучения) и хочу найти перекрывающиеся билеты.

Комбинация LineId + ScheduleId + ForDate однозначно определяет, что билет предназначен для определенного автобуса в определенную дату и время отправления.

Проблема, с которой я сталкиваюсь, состоит в том, чтобы определить, учитывая начальное и конечное местоположение, перекрываются ли два билета для одной или нескольких остановок.

Сущность LineStopStation содержит информацию о StopId и Order, в которых она посещается во время автобусной поездки. Таким образом, перекрывающиеся билеты в какой-то момент будут иметь одинаковый номер Order (если только это не последняя остановка, что означает, что пассажир выходит из автобуса).

Итак, у меня есть LineId, ScheduleId, StartId и EndId, где starId и endId соответствуют LineStopStation.StopId, поэтому в конечном итоге я могу получить из них Орден вот так.

        int startStationOrder = _context.LineStopStations
            .First(l => l.LineId == lineId && l.StopId == startId).Order;

        int endStationOrder = _context.LineStopStations
            .First(l => l.LineId == lineId && l.StopId == endId).Order;

Поэтому я вполне убежден, что, имея всю эту информацию, я смогу найти, есть ли в таблице TicketStop билет, который совпадает с рассматриваемыми данными. TicketStop работает так - если кто-то купил билет на 3 остановки у меня там будет три записи с одинаковым TicketId и три разных LineStopStationId.

Я чувствую, что этот вопрос стал больше, чем нужно. Итак, в основном у меня есть это:

 public List<Seat> GetAvailableSeats(int lineId, int scheduleId, int startId, int endId, DateTime forDate)
 {
   int startStationOrder = _context.LineStopStations
       .First(l => l.LineId == lineId && l.StopId == startId).Order;

   int endStationOrder = _context.LineStopStations
       .First(l => l.LineId == lineId && l.StopId == endId).Order;

   var reservedSeats = _context.TicketStops
       .Where(t => t.Ticket.ScheduleId == scheduleId)
       .Where(t => t.Ticket.ForDate == forDate)
       //basically I don't know how to proceed here.
       //In pseudo code it should be something like:
       .Where(t => t.Min(Order) >= endStationOrder || t.Max(Order) <= startStationOrder

 }

Но обс. это не так LINQ работает. Итак, как я могу найти все билеты, в которых этот диапазон пересекается?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
44
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Без глубокого анализа вашей модели, возможно, что-то подобное могло бы дать вам представление?

var reservedSeats = _context.TicketStops
                            .GroupBy(t => new { t.Ticket.ScheduleId, t.Ticket.ForDate })
                            .Where(tg => tg.Key == new { ScheduleId = scheduleId, ForDate = forDate })
                            .Where(tg => tg.Min(t => t.LineStopStation.Order) >= endStationOrder || tg.Max(t => t.LineStopStation.Order) <= startStationOrder);

Вы также можете сначала отфильтровать и сделать пустой GroupBy:

var reservedSeats = _context.TicketStops
                            .Where(t => t.Ticket.ScheduleId == scheduleId && t.Ticket.ForDate == forDate)
                            .GroupBy(t => 1)
                            .Where(tg => tg.Min(t => t.LineStopStation.Order) >= endStationOrder || tg.Max(t => t.LineStopStation.Order) <= startStationOrder);

Чтобы вернуть все SeatId, вам просто нужно выбрать их из группы.

var reservedSeats = _context.TicketStops
                            .Where(t => t.Ticket.ScheduleId == scheduleId && t.Ticket.ForDate == forDate)
                            .GroupBy(t => 1)
                            .Where(tg => tg.Min(t => t.LineStopStation.Order) >= endStationOrder || tg.Max(t => t.LineStopStation.Order) <= startStationOrder);
                            .SelectMany(tg => tg.Select(t => t.Ticket.SeatId));

Это было то, что я искал. Кажется, я сам разобрался, но если у вас есть время, еще один вопрос. После группировки и фильтрации я хочу взять фактические SeatId, которые следуют в этих условиях. Правильно ли написать: .Select(tg => tg.First().Ticket.SeatId) Вроде возвращает правильный результат, но, как видите, я не владею LINQ\GroupBy. В любом случае, спасибо.

Leron_says_get_back_Monica 03.06.2019 07:09

Похоже, у вас будет несколько билетов в группе tg — будут ли все SeatId одинаковыми, или вам нужно принять решение, какой из них вернуться? Это, безусловно, сработает, но будет возвращаться только первое совпадение в (возможно) случайном порядке.

NetMage 03.06.2019 13:06

Да, я понял это. SeatId разные, поэтому получить только один недостаточно, но подход GroupBy — это то, что нужно, ИМО. Однако, поскольку это одна из самых важных частей в моей реализации, я решил также включить min/max Order в Ticket Entity, что денормализует структуру, но делает ее намного проще в использовании. Так что пока я не буду прилагать к этому больше усилий. Если вам интересно, я буду признателен за решение с GroupBy, где возвращаются все SeatId.

Leron_says_get_back_Monica 03.06.2019 13:31

@Leron Это просто - вам просто нужно выбрать их из каждой группы. Я использовал SelectMany, чтобы отменить группу, но вы также можете сделать вложенную Select, а затем First(). LINQ to Entities может преобразовывать одно в другое.

NetMage 03.06.2019 18:57

Другие вопросы по теме