可惜您忘记提供课程的相关部分。现在我们必须从您的 SQL 中提取它们,希望我们得出正确的结论。下次考虑添加你的类定义。
此外,了解您的 SQL 查询所基于的需求可能会很方便,现在我们必须从您的 SQL 中提取需求。
要求:Id == 2560378 的 StopTime 属于一个行程。此行程有零个或多个路线。从此 StopTime 中,获取具有 StartDate = ... 和 TuesDay == 1 的 Trip 及其所有路线。从生成的项目中获取属性 DepartureTime、StopTime、HeadSign、...
类
在我看来,您的数据库有 StopTimes、Stops、Trips 和 Calendars 表。
显然它们之间存在某种关系。有些可能是一对多,有些可能是多对多,或一对零或一。从您的查询中很难确定这些关系。
在我看来,每个Trip 都有零个或多个StopTimes,每个StopTime 恰好属于一个Trip(一对多)。在StopTimes 和Stops 之间也存在一对多:每个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;}
}
等: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);
}