Перевод SQL-запроса в Linq для использования с Entity Framework - PullRequest
0 голосов
/ 22 января 2019

ищу некоторую помощь в этом - я новичок в Entity Framework / метод запросов Linq. Приведенный ниже оператор SQL извлекает нужные мне данные из базы данных SQL Server.

Может кто-нибудь помочь с тем, как это должно выглядеть в Linq, чтобы я мог понять, как это работает?

SELECT st.departure_time, t.trip_headsign, r.route_short_name, r.route_long_name
FROM stop_times st
LEFT JOIN stops s ON s.stop_id = st.stop_id
LEFT JOIN trips t ON t.trip_id = st.trip_id
LEFT JOIN routes r ON r.route_id = t.route_id
LEFT JOIN calendar c ON c.service_id = t.service_id
WHERE st.stop_id = '2560378' AND c.start_date <= 20190122 AND c.end_date >= 20190122 AND c.tuesday = 1
ORDER BY st.departure_time ASC

Использование Entity, как показано ниже, и т.д., выберет все остановки

using (var db = new TestEntities())
{
    var query = from b in db.Stops select b;
}

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Увы, вы забыли предоставить соответствующие части ваших классов. Теперь мы должны извлечь их из вашего SQL, надеясь сделать правильные выводы. В следующий раз подумайте о добавлении определений классов.

Кроме того, может быть полезно узнать требование, на котором вы основали свой запрос SQL, теперь нам нужно извлечь это требование из вашего SQL.

Требование: StopTime с Id == 2560378 относится к одной Поездке. Эта поездка имеет ноль или более маршрутов. Из этого StopTime возьмите Trip и все ее маршруты, имеющие StartDate <= ... и EndDate> = ... и ВтDay == 1. Из полученных элементов возьмите свойства DepartureTime, StopTime, HeadSign, ...

Классы

Мне кажется, что в вашей базе данных есть таблицы StopTimes, Stops, Trips и Calendars.

Очевидно, между ними есть связь. Некоторые могут быть один ко многим, некоторые могут быть многими ко многим или один к нулю или одному. По вашему запросу сложно определить эти отношения.

Мне кажется, что каждый Trip имеет ноль или более StopTimes, и каждый StopTime принадлежит ровно одному Trip (один ко многим). Между StopTimes и Stops также существует отношение один ко многим: каждый StopTime имеет ноль или более Stops, и каждый Stop принадлежит ровно одному StopTime. Кроме того: Trip имеет несколько Routes, а также несколько Calendars.

Может быть, некоторые из этих отношений не один-ко-многим, но многие-ко-многим или один-к-одному. Принцип остается прежним.

Если вы следовали первым соглашениям кода платформы e ntity, ваши классы будут похожи на следующие:

class Trip
{
    public int Id {get; set;}
    ...

    // Every Trip has zero or more StopTimes (one-to-many):
    public virtual ICollection<StopTime> StopTimes {get; set;}
    // Every Trip has zero or more Routes (one-to-many):
    public virtual ICollection<Route> Routes {get; set;}
    // Every Trip has zero or more Calendars (one-to-many):
    public virtual ICollection<Calendar> Calendars {get; set;}
}

class StopTime
{
    public int Id {get; set;}
    ...
    // Every StopTime belongs to exactly one Trip using foreign key:
    public int TripId {get; set;}
    public virtual Trip Trip {get; set;}

    // Every StopTime has zero or more Stops (one-to-many):
    public virtual ICollection<Stop> Stops {get; set;}
}

class Route
{
    public int Id {get; set;}
    ...

    // every Route belongs to exactly one Trip (using foreign key)
    public int TripId {get; set;}
    public virtual Trip Trip {get; set;}
}

И т.д .: Stops и Calendars будут очень похожи.

В каркасе сущностей столбцы ваших таблиц представлены не виртуальными свойствами. Виртуальные свойства представляют отношения между таблицами.

Поскольку я следовал соглашениям, структура сущностей способна обнаруживать первичные и внешние ключи и отношения между таблицами. Нет необходимости ни в атрибутах, ни в беглости API. Если вы хотите использовать разные идентификаторы, могут потребоваться атрибуты или свободный API.

Запрос с использованием виртуальных свойств

Как только вы правильно спроектировали свои классы, особенно отношения между таблицами (виртуальная ICollection), ваш запрос будет простым:

var result = dbContext.StopTimes
    .Where(stopTime => stopTime.Id == 2560378)
    .SelectMany(stopTime => stopTime.Trip.Routes
         .Where(route => route.StartDate <= 20190122 && route.EndDate >= 20190122)
    (stopTime, route) => new
    {
        DepartureTime = stopTime.DepartureTime,
        TripHeadSign = stopTime.Trip.HeadSign,
        Route = new
        {
            ShortName = route.ShortName,
            LongName = route.LongName,
        },

        // or, if you don't want a separate Route Property:
        RouteShortName = route.ShortName,
        RouteLongName = route.LongName,
    })
    .OrderBy(item => item.DepartureTime);

Поскольку структура сущностей знает мои отношения, она знает, какую (группу) объединяет для выполнения при использовании виртуальных свойств.

Запрос на фактическое соединение

Некоторые люди действительно предпочитают использовать соединения. Хорошо, если вы можете убедить своего руководителя проекта, что следующее лучше читаемо / тестируемо / поддерживаемо:

var result = dbContext.StopTimes
    .Where(stopTime => stopTime.Id == 2560378) // keep only StopTimes with Id == ...
    .Join(dbContext.Trips,                     // Join with Trips
    stopTime => stopTime.TripId,               // from every StopTime take the TripId
    trip => trip.Id                            // from every Trip take the Id,
    (stopTime, trip) => new                    // keep only the properties I need
    {
        DepartureTime = stopTime.DepartureTime,
        TripHeadSign = trip.HeadSign
        TripId = trip.Id,                      // Id is used in the next join
    }
    // join this joinResult with the eligible routes:
    .Join(dbContext.Routes
          .Where(route => route.StartDate <= ... && route.EndDate >= ...)
    firstJoinResult => firstJoinResult.TripId,
    route => route.TripId,
    (firstJoinResult, route) =>  new
    {
        DepartureTime = firstJoinResult.DepartureTime,
        TripHeadSign = firstJoinResult.TripHeadSign,
        Route = new
        {
            ShortName = route.ShortName,
            LongName = route.LongName,
        },
    })
    .OrderBy(item => item.DepartureTime);
    }
0 голосов
/ 22 января 2019

Я начну вас с вашей первой пары соединений, чтобы вы могли получить остальное.Вам также необходимо завершить предложение WHERE.

            var query = (from ST in db.stop_times
                         join S in db.stops on ST.stop_id equals S.stop_id into Ss
                         from S in Ss.DefaultIfEmpty()
                         join T in db.trips on ST.trip_id equals T.trip_id into Ts
                         from T in Ts.DefaultIfEmpty()
                         where ST.stop_id = '2560378'
                         select new YourCustomObject
                         {
                             DepartureTime = ST.departure_time,
                             TripHeadsign = T.trip_headsign
                         }).OrderBy(x => x.DepartureTime);

Как вы можете видеть, LEFT JOIN:

join T in db.trips on ST.trip_id equals T.trip_id into Ts
                             from T in Ts.DefaultIfEmpty()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...