【问题标题】:Translating SQL Query to Linq for use with Entity Framework将 SQL 查询转换为 Linq 以与实体框架一起使用
【发布时间】:2019-01-22 10:56:15
【问题描述】:

在这方面寻求帮助 - 我是 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

使用像下面这样的实体会选择所有的站点

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

【问题讨论】:

标签: c# entity-framework linq


【解决方案1】:

我将从您的前几次加入开始,以便您完成其余的工作。您还需要完成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()

【讨论】:

  • 谢谢 - 我能够根据您的示例构建完整的查询。
【解决方案2】:

可惜您忘记提供课程的相关部分。现在我们必须从您的 SQL 中提取它们,希望我们得出正确的结论。下次考虑添加你的类定义。

此外,了解您的 SQL 查询所基于的需求可能会很方便,现在我们必须从您的 SQL 中提取需求。

要求:Id == 2560378 的 StopTime 属于一个行程。此行程有零个或多个路线。从此 StopTime 中,获取具有 StartDate = ... 和 TuesDay == 1 的 Trip 及其所有路线。从生成的项目中获取属性 DepartureTime、StopTime、HeadSign、...

在我看来,您的数据库有 StopTimes、Stops、Trips 和 Calendars 表。

显然它们之间存在某种关系。有些可能是一对多,有些可能是多对多,或一对零或一。从您的查询中很难确定这些关系。

在我看来,每个Trip 都有零个或多个StopTimes,每个StopTime 恰好属于一个Trip(一对多)。在StopTimesStops 之间也存在一对多:每个StopTime 有零个或多个Stops,每个Stop 恰好属于一个StopTime。此外:一个Trip 有几个Routes 和几个Calendars

其中一些关系可能不是一对多,而是多对多或一对一。原理还是一样的。

如果您已关注 entity framework code first conventions,,您的课程将类似于以下内容:

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;}
}

等:StopsCalendars 将非常相似。

在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系。

由于我遵循约定,实体框架能够检测主键和外键以及表之间的关系。不需要属性,也不需要流畅的 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);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多