Entity Framework、MVC 3、LINQ To Entities 中的 OrderBy

4

我有以下查询:

model.Page = db.Pages
    .Where(p => p.PageId == Id)
    .Include(p => p.Series
                   .Select(c => c.Comics
                                 .Select(col => col.Collection)))
    .SingleOrDefault();

这很好用,但现在我需要按“阅读顺序”属性对漫画进行排序。

我尝试过:

model.Page = db.Pages
    .Where(p => p.PageId == Id)
    .Include(p => p.Series.Select(c => c.Comics.OrderBy(o => o.ReadingOrder)
                          .Select(col => col.Collection)))
    .SingleOrDefault();

但这会导致以下错误:
引用的Include路径表达式必须是类型上定义的导航属性。对于引用导航属性,请使用点路径,对于集合导航属性,请使用“Select”运算符。参数名称:路径。
你有什么想法吗?
谢谢!
public class Page
{
    public int PageId { get; set; }
    public string Title { get; set; }
    public ICollection<Series> Series { get; set; }
}

public class Series
{
    public int SeriesId { get; set; }
    public int PageId { get; set; }
    public string Title { get; set; }
    public Page Page { get; set; }
    public ICollection<Comic> Comics { get; set; }
}

public class Comic
{
    public int ComicId { get; set; }
    public string Title { get; set; }
    public int ReadingOrder { get; set; }
    public string Subtitle { get; set; }
    public int CollectionId { get; set; }
    public Collection Collection { get; set; }

}

public class Collection
{
    public int CollectionId { get; set; }
    public string Title { get; set; }
    public ICollection<Comic> Comics { get; set; }
}
2个回答

10

异常信息"……包含路径表达式必须引用导航属性……"的基本意思是c.Comics.OrderBy不是一个导航属性。(我认为这是合理的投诉。)

实际上,EF不支持在贪婪加载语句(Include)中应用排序(以及过滤)。(这是事实。)

那么,你可以怎么做呢?

选项1:

在加载实体后,在内存中进行排序:

model.Page = db.Pages
    .Where(p => p.PageId == Id)
    .Include(p => p.Series.Select(c => c.Comics
                          .Select(col => col.Collection)))
    .SingleOrDefault();

if (model.Page != null)
{
    foreach (var series in model.Page.Series)
        series.Comics = series.Comics.OrderBy(c => c.ReadingOrder).ToList();
}

虽然不太美观,但因为您只通过id加载单个Page对象,所以它可能比以下选项更快(在内存中使用LINQ to Objects),如果SeriesComics集合不是特别长的话。

选项2:

将查询分解成混合了急切加载和显式加载的几个部分:

model.Page = db.Pages
    .Where(p => p.PageId == Id)
    .Include(p => p.Series) // only Series collection is included
    .SingleOrDefault();

if (model.Page != null)
{
    foreach (var series in model.Page.Series)
        db.Entry(series).Collection(s => s.Comics).Query()
          .Include(c => c.Collection)
          .OrderBy(c => c.ReadingOrder)
          .Load(); // one new DB query for each series in loop
}

方案三:

投影?

这里这里讨论了多个导航属性的复杂Include链所带来的问题。它可能会加载大量重复的数据。 Include可以确保只有一个数据库往返,但可能需要传输更多的数据。显式加载需要多个往返,但总体上可能需要更少的数据。

(我知道,我给了你这个 Include...Select...Select...Select... 链,但我怎么知道你会认真对待呢:)。根据嵌套集合的大小,它仍然可能是最佳选择。)


Slauma - 你能接受雇佣吗?非常感谢你抽出时间来研究这个问题。目前我选择了选项1,只是为了速度,但会在某个时候研究其他两个选项。再次感谢! - Sniffer
@Sniffer:我不建议你雇佣那些写一个带问号的选项3的人 :) 实际上,我不确定在这些多层嵌套的集合中是否有任何用处。 - Slauma

0

我头脑中想到的,未经测试:

model.Page = db.Pages
               .Where(p => p.PageId == Id)
               .Include(p => p.Series
                              .Select(c => c.Comics
                                            .Select(col => col.Collection)
                                            .OrderBy(o => o.ReadingOrder)))
               .SingleOrDefault();

谢谢@Dennis,但那是在集合上排序,而不是在漫画上。 - Sniffer
嗯,你说得对。但是如果没有实际看到你的模型,我就不知道了。抱歉。 - Dennis Traub
嗨@Dennis - 说得好。我已经将我的模型添加到问题文本中了。 - Sniffer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接