【问题标题】:Order column by a separate `List<string>` variable通过单独的 `List<string>` 变量对列进行排序
【发布时间】:2016-08-31 20:03:34
【问题描述】:

我需要根据另一个列表中元素的索引对实体框架查询的结果进行排序。

我尝试了其他地方的建议,比如

.ThenBy(m=>list.IndexOf(m.Term))

但我收到 HTTP 500 错误,所以我想知道我是否遗漏了什么。调试时,我得到了这个内部异常。

LINQ to Entities 无法识别方法 'Int32 IndexOf(System.String)' 方法,而且这个方法不能翻译 到商店表达式中。

特别是,我正在考虑这种情况。

private IEnumerable<MiaLog1A> PopulateQuery(string selectedCampus)
{
    var list = new List<string> {"No Longer Alphabetical","Fall","Mid","Spring"};
    return _db.MiaLog1A.Where(m => m.Campus == selectedCampus)
            .OrderBy(m => m.StudentName)
            .ThenBy(m=>m.Term)  
                    /* I would like to sort "Term" by the 
                     * order of "list".
                     */
        .AsEnumerable();
}

有没有办法以这种方式排序,或者我是否仅限于按字典排序或创建连接?

【问题讨论】:

  • this question 的答案可能会有所帮助。
  • @sbat 不,这个问题正在内存中排序,OP 正在使用 EF,它试图将可查询的语句重写为 sql 语句。 500 错误说明了这一点。
  • @Will 我知道这是一个老问题,但发布了一个使用 IQueryable 的答案。

标签: c# entity-framework linq


【解决方案1】:

这应该可以解决问题:

private IEnumerable<MiaLog1A> PopulateQuery(string selectedCampus)
{
    var list = new List<string> {"Fall","Mid","Spring"};
    return _db.MiaLog1A.Where(m => m.Campus == selectedCampus)
        .AsEnumerable()
        .OrderBy(m => m.StudentName)
        .ThenBy(m=> list.IndexOf(m.Term));
}

【讨论】:

  • 调试后,我得到这个内部异常:“LINQ to Entities 无法识别方法 'Int32 IndexOf(System.String)' 方法,并且该方法无法转换为存储表达式。 "
  • 啊,对,它就是您正在使用的 SQL-Linq。不知道它在那里是如何工作的。但应该与 Enumberable-LINQ 一起使用。所以也许考虑在.AsEnumerable()之后排序
  • 编辑了我的答案,以便在构建和处理查询后进行排序。
  • 这就是关键。我一直忘记我必须在 .AsEnumerable()... 之后进行一些操作
  • @Rubix_Revenge 了解 EF 和可查询对象背后的魔力很重要。有很多关于这个主题的信息,所以如果您不熟悉 EF 如何从您的 linq 语句生成 sql,请花一个小时阅读它。你会感谢自己的。
【解决方案2】:

这是一种疯狂的方法,但它不会退回到IEnumerable

var db = new BloggingContext();

var list = (new List<string> { "FR", "EN" }).
    Select((s,i)=> $"select '{s}' as {nameof(OrderIndex.Name)},{i} as {nameof(OrderIndex.Id)}");

var order = db.Set<OrderIndex>().FromSql(String.Join(" union ",list));

var orderedItems = from post in db.Posts
                   join ln in order on post.Lang equals ln.Name into lnPost
                   from od in lnPost.DefaultIfEmpty()
                   orderby od.Id
                   select post;
var data = orderedItems.ToList();

您可以找到 BloggingContext here 的定义我刚刚为语言代码添加了一个 Lang 字段。这将适用于 SQLite、SqlServer 和 MySQL for oracle,您需要添加 from dual 由于 EF 核心有多糟糕,您需要对 OrderIndex 执行与 SQL 视图相同的过程。在 EF 6 中,使用 SqlQuery 有一种更好的方法,您不需要像使用 EF 核心那样进行注册。 创建的查询是

SELECT "post"."PostId", "post"."BlogId", "post"."Content", "post"."Date", "post"."Lang", "post"."Title", "ln"."Id", "ln"."Name"
    FROM "Posts" AS "post"
    LEFT JOIN (
        select 'FR' as Name,0 as Id union select 'EN' as Name,1 as Id
    ) AS "ln" ON "post"."Lang" = "ln"."Name"
    ORDER BY "ln"."Id", "post"."Lang"

编辑: 只是记住了一种不同的方法,它没有那么疯狂,但可能会更好。

var lang = new List<string> { "FR", "EN" };    
var orderedItems = from post in db.Posts
                   orderby (lang[0] == post.Lang) ? 
                   0 :((lang[1] == post.Lang) ? 1 : 2)
                   select post;

var param = Expression.Parameter(typeof(Post));

var order = lang.Select((s, i) => new { s, i })
    .Aggregate((Expression)Expression.Constant(lang.Count), (agg, i) =>
        Expression.Condition(
            Expression.Equal(Expression.Property(param,nameof(Post.Lang)),
            Expression.Constant(i.s)),
            Expression.Constant(i.i),
            agg));
var exp = Expression.Lambda<Func<Post, int>>(order, param);
var data = db.Posts.OrderBy(exp).ToList();

还有 SQL

SELECT "p"."PostId", "p"."BlogId", "p"."Content", "p"."Date", "p"."Lang", "p"."Title"
    FROM "Posts" AS "p"
    ORDER BY CASE
        WHEN "p"."Lang" = 'EN'
        THEN 1 ELSE CASE
            WHEN "p"."Lang" = 'FR'
            THEN 0 ELSE 2
        END
    END

我仍然认为这种疯狂的方式对我不知道的事情很有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-07
    • 1970-01-01
    • 2010-10-17
    相关资源
    最近更新 更多