Linq - 包含和过滤 - 嵌套集合

时间:2017-04-21 19:48:56

标签: asp.net-mvc entity-framework linq

我正在使用mvc应用程序,人们可以搜索火车路线。我使用实体框架使我的数据库代码优先,但我似乎无法弄清楚如何提出一些更复杂的查询。

我有一组我在viewmodel中使用的路线。所以第一步是询问哪些路线有一个特定的起点和终点站。然后我想包括某个日期设置为true的日程表(以及该日程表的开始日期和结束日期匹配的位置)。这与一系列旅行相关联(我使用此表会导致路线在一天中多次运行)。从这里我可以找到所有匹配的站点,其中包含来自tableHasStations的到达和离开时间。

所以我想的是:

public IEnumerable<Route> Search(DateTime date, int? departure, int? arrival)
{
   var day = date.DayOfWeek.ToString();
   return db.Routes.Where(r => r.DepartureStationID == departure && r.ArrivalStationID == arrival)
                   .Include(s => s.Train)
                   //using linq.dynamic here 
                   .Include(r => r.Schedule.where(day + "==" + true)
                   .Include(sch => sch.trip.where(date > sch.DepartureTime)
                   .Include(route => route.RouteHaseStations)
                   .Include(st => st.Stations)
}

但这是不能正常工作的。这是我的模特:

public class Route
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int RouteID { get; set; }
    public String RouteName { get; set; }
    [ForeignKey("Train")]
    public int TrainID { get; set; }
    [ForeignKey("Station")]
    public int DepartureStationID { get; set; }
    [ForeignKey("Station")]
    public int ArrivalStationID { get; set; }
    public virtual ICollection<Schedule> Schedule { get; set; }
    public virtual Train Train { get; set; }
    public virtual Station DepartureStation { get; set; }
    public virtual Station ArrivalStation { get; set; }
}

public class Station
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public  int StationID { get; set; }
    [Display(Name = "Station")]
    public  String Name { get; set; }
    public int  Platforms { get; set; }
    public float Latitude { get; set; }
    public float Longitude { get; set; }
    public virtual ICollection<RouteHasStation> RouteHasStation { get; set; }
}

public class Train
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public  int TrainID { get; set; }
    public  String Name { get; set; }
    public virtual ICollection<Route> Route { get; set; }
}

public class Schedule
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int ScheduleID { get; set; }
    [ForeignKey("Route")]
    public int RouteID { get; set; }
    public Boolean Monday { get; set; }
    public Boolean Tuesday { get; set; }
    public Boolean Wednesday { get; set; }
    public Boolean Thursday { get; set; }
    public Boolean Friday { get; set; }
    public Boolean Saturday { get; set; }
    public Boolean Sunday { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public virtual ICollection<Trip> Trip { get; set; }
    public virtual Route Route { get; set; }
}

public class Trip
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int TripID { get; set; }
    [ForeignKey("Schedule")]
    public int ScheduleID { get; set; }
    public DateTime DepartureTime { get; set; }
    public virtual ICollection<RouteHasStation> RouteHasStation { get; set; }
}

public class RouteHasStation
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int RouteHasStationID { get; set; }
    [ForeignKey("Station")]
    public int StationID { get; set; }
    public virtual Station Station { get; set; }
    [ForeignKey("Trip")]
    public int TripID { get; set; }
    public virtual Trip Trip { get; set; }
    [DataType(DataType.Time)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:HH:mm}")]
    public DateTime? Arrival { get; set; }
    [DataType(DataType.Time)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:HH:mm}")]
    public  DateTime? Departure { get; set; }
    public int Platform { get; set; }
    public float Distance { get; set; }
}

1 个答案:

答案 0 :(得分:1)

除了输入错误外,您的程序还有几个问题。

声明

.Include(r => r.Schedule.where(day + "==" + true)

没有正确的谓词。即使您能够将其更改为谓词,LINQ to SQL也不知道如何将其作为查询处理,因为您需要根据变量日调用Schedule类中的其他过程。 / p>

由于与include的结合,我不确定你想要什么。根据您的描述,我想说您希望计划在指定搜索日期运行的所有路线(带有某个出发站和到站)。

我不确定您是使用现有数据库,还是仍在开发它。在后一种情况下,请考虑更改Schedule类,以便Schedule是一个标记为枚举的枚举

[Flags]
public enum MyDayOfWeek
{
    Sunday = 1 << System.DayOfWeek.Sunday,         // 0x01
    Monday = 1 << System.DayOfWeek.Monday,         // 0x02
    Tuesday = 1 << System.DayOfWeek.Tuesday,       // 0x04
    Wednesday 1 << System.DayOfWeek.Wednesday,     // 0x08
    Thursday 1<< System.DayOfWeek.Thursday,        // 0x10
    Friday = 1 << System.DayOfWeek.Friday,         // 0x20
    Saturday = 1 << System.DayOfWeek.Saturday,     // 0x40
}

public class Schedule
{
    public int ScheduleID { get; set; }
    public int RouteID { get; set; }
    public MyDayOfWeek RunDays { get; set; }
    public virtual ICollection<Trip> Trips {get; set;}
    ...

RunDays是计划运行路线的所有日期的or-flagged变量,例如,如果计划仅在周末运行:

RunDays = DayOfWeek.Saturday | DayOfWeek.Sunday;

获取计划在给定DateTime上运行的所有路线的查询将如下:

System.DayOfWeek dayOfWeek = day.DayOfWeek;
MyDayOfWeek myDayOfWeek = (MyDayOfWeek)( 1 << (int)dayOfWeek);

var routesRunningOnDate = db
    .Where(route => route.DepartureStation etc
        && (route.RunDays & myDayOfWeek) == myDayOfWeek)

Linq to SQL将知道如何处理它。

如果您无法更改数据库的型号,并且您仍然无法使用此计划的定义,则无法执行此AsQueryable,但是可以执行它AsEnumerable 1}},在你的本地记忆中。

如果您想使用Schedule.Monday,Schedule.Tuesday等功能检查您的路线是否安排在某一天的某一天,我建议您为Schedule类定义和扩展:

public static class ScheduleExtensions
{
    // returns whether schedule scheduled on date:
    public static bool IsScheduled(this Schedule schedule, DateTime date)
    {
        switch (date.DayOfWeek)
        {
            case DayOfWeek.Sunday:
                return schedule.Sunday;
            case DayOfWeek.Monday:
                return chedule.Monday;
            ...
        }
    }
}

用法:

db.Routes.AsEnumerable().Where (route => route.Schedule.IsScheduled(date));

此外,您似乎无法正确包含所有内容。你所有的参数r,s,sch,route,st都应该是路由,而实际上你的意思是你的sch是时间表等。

请注意,只要您没有真正列举您的序列(并且您不会丢弃DbContext),您就不需要包含语句。如果您没有专门询问某个值,但是您希望它们位于本地内存中,则需要包含语句。

如果您有一个IQueryable,它不使用您表的某个字段,并且您使用此字段,则在枚举之前,该字段会自动添加到查询中,从而添加到您的SQL查询中:

var query1 = routes.Where(r.DepartureStationID == departure && r.ArrivalStationID == arrival);
var query2 = query1.Where(route => route.Schedule.Trips...)

如果没有额外的包含,LINQ to SQL就知道需要使用Schedule表进行连接。

我想你想要以下路线:

(1)所有在DepartureStation上启动并到达ArrivalStation的路线  (2)计划在一天运行的AND  (3)安排在(不是?)Schedule.DepartureTime之后的AND  (4)确保你有来自route.RouteHasStations的所有电台

不确定Schedule.DepartureTime是否表示此计划有效的第一天(因此,激活日期不是更好的名称吗?)或者DepartureTime是否是列车在计划日期离开的时间。

在后一种情况下声明

.Include(sch => sch.trip.where(date > sch.DepartureTime)

没有做你想做的事情:即使出发时间晚于你约会的时间,它也会返回你日期之后的日期安排的所有时间表

db.Routes.Where(route => route.DepartureStaionId == departure
    && route.ArrivalStationId == arrival
    && route.Schedule.IsScheduled(date)
    && date > route.Trips.DepartureTime)
    // you don't need to include Schedule and Trips explicitly
    // unless you need their fields outside this query

    .Include(route => route.Train)
    .Include(route => route.HasTrips)
    // only include these if you need their fields outside this query
    // if the only thing from train you want is Train.Name,
    // use route.Train.Name in a select after the Where