【问题标题】:How to sort List using custom column values in linq?如何使用 linq 中的自定义列值对列表进行排序?
【发布时间】:2020-07-16 05:09:17
【问题描述】:

Net 核心、Ef 核心和 linq。我有列状态表。它保存值 New、In-Progress 和 Closed。当我将使用 New 查询所有行时,应该首先出现,然后是进行中并关闭。下面是我的代码。

var Requests = await this.RequestRepository.GetAsync(x => x.IsActive == true && x.CreatedBy == LoggedInUser, null, x => x.Country).ConfigureAwait(false);

下面是我的 GetAsync 方法

public async Task<IEnumerable<T>> GetAsync(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includes)
        {
            IQueryable<T> query = this.dbSet;
            foreach (Expression<Func<T, object>> include in includes)
            {
                query = query.Include(include);
            }

            if (filter != null)
            {
                query = query.Where(filter);
            }

            if (orderBy != null)
            {
                query = orderBy(query);
            }

            return await query.ToListAsync().ConfigureAwait(false);
        } 

所以我希望所有状态为 New 的行应该首先出现,然后是 In-Progress 并关闭。无论如何,我都找不到解决办法。有人可以帮我写这个。任何帮助,将不胜感激。谢谢

【问题讨论】:

  • IQueryable 实现了IEnumerable,那么是什么阻止您使用 LINQ 中的 OrderBy 方法?
  • 您好,我需要订购 New、InProgress 和已关闭的订单,我该怎么做?
  • this 答案的改编版本应该可以工作。虽然 EF 很可能会为此切换到客户端评估(客户端和服务器端评估之间的区别在详细解释 here
  • 您将 null 传递给 orderBy 那么您为什么期望结果是有序的?
  • 我不太确定拥有这种“帮助”方法能给你带来什么,而不是直接使用 LINQ - 你为什么要使用它?以使您的代码可读为代价来节省一些击键?

标签: c# linq asp.net-core ef-core-2.0


【解决方案1】:

如果您放弃这种“帮助”方法并直接使用 LINQ,您的生活可能会轻松很多。看看它的可读性有多明显:

var apps = db.FundingApplications
  .Include(x => x.Applicant)
  .Where(x => x.ApplicationDate.Year == DateTime.Now.Year)
  .OrderBy(x => (int)x.Status)

(为了简洁,我跳过了等待/异步,不是因为我提倡同步)

状态当然是一个枚举,例如:

enum Status{
  New, Inprogress, Closed
}

如果你的枚举不是这样的数字顺序,而是例如

enum Status {
  New = 20,
  InProgress = 1,
  Closed = 99
}

然后,您可以在 ToString 之后通过降序对其进行排序(您会注意到名称是按字母倒序排列的)或使用映射例如 d[New] = 0, d[InProgress] = 1 ... 的字典创建映射,或者执行(内联) if在 OrderBy 中设置。

OrderByDescending(x => x.Status.ToString("g"));

var d = new Dictionary<status, int>() {
  {New, 0}, {InProg,1}, {Closed,2}
};
OrderBy(x => d[x.Status]);

OrderBy(x => x.Status == Status.New ? 0 : (x.Status == Status.InProg ? 1 : 2))

不过,只有最后一个有真正的机会在服务器上执行,并且只有在您不重命名它们或添加破坏排序的新名称时,按其名称排序才会起作用。

我也很欣赏另一个答案中给出的 OrderBy/ThenBy 并认为这很好,但我认为应该注意测试它是在服务器还是客户端上执行,因为它执行布尔比较和大量 db不要使用布尔值作为数据类型,否则它们可能会将 falsy (0) 排序在 truey (1) 之前

OrderBy(x => x.Status == New)

可能就在那时,它将所有已关闭和 InProg 排序在 New 之前,因为如果由服务器评估,布尔值的结果对于 InProg/closed 为 0,对于 new 为 1。 MySql 和 Postgres 将是我的首选,因为我担心这会被服务器评估,因为它们确实允许在其他 dbs 不允许的上下文中使用布尔类型值。

因此,请注意 a) 这种排序正在评估您期望和想要的位置(客户端/服务器),并且 b) 如果它真的在服务器上评估服务器是否按照您的需要进行排序


编辑,刚刚看到您的评论表明 Status 不是枚举,而是一组字符串常量??

如果您的 DB 值是字面意义上的字符串并且状态从来不是枚举,那么您可以通过降序对它们进行排序

OrderByDescending(x => x.Status)

【讨论】:

  • 确实如此。而且,这样你就可以使用.ThenBy{OrderBy|ThenBy}Descending,这在使用助手时会丢失。
  • 感谢您的回答,但对我来说这也有效 var siteAssessmentRequests = await this.SiteAssessmentRequestRepository.GetAsync(x => x.IsActive == true && x.CreatedBy == LoggedInUser, x =>x. OrderByDescending(x=>x.Status == "New").ThenByDescending(x => x.Status == "In Progress").ThenByDescending(x => x.Status == "Closed"), x => x .Country).ConfigureAwait(false);那么哪个最合适?这个或者你分享了一个
  • @Niranjangodbole 很公平,我忘了你传递的是 Func, IOrderedQueryable>` 而不是 Expression&lt;Func&lt;T, K&gt;&gt;。但是,正如这个答案所指出的,您的版本更难阅读。
  • 呃,他们有不同的目的。我是说你应该把它扔掉,因为 99.9% 了解 LINQ 的开发人员将能够直接拿起我的版本并像一个句子一样阅读它并告诉你它的作用。如果您将您的评论作为关于 SO 的问题发表在“这有什么用”,每个人都会说“发布 GetAsync 的定义”,因为它不可读或无法自我记录。这也不是我们使用存储库模式的方式。当我们使用存储库时,我们会给出一个明确命名的方法,例如 GetCurrentYearsApplicationsWithApplicantsOrderedByStatus 并在其中执行所有 LINQ
  • 您所拥有的只是某种可能执行过滤器、包含或排序的包装器方法,但除了另一种方法来记住的定义是为了理解它所做的事情,这是由它的论点高度上下文驱动的。我建议你应该直接使用 LINQ,因为你没有节省很多(如果有的话)击键,而且你让代码更难阅读和理解,事实上很难,你必须来寻求帮助让它去。下一个维护它的人可能也必须这样做
【解决方案2】:

我已根据您的要求制作了示例代码。它在我的工作正常。请参考这里。希望这也适用于您的代码。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Test
{
    public class Program
    {
        public static void Main(string[] args)
        {
            List<Name> list = new List<Name>
            {
                new Name { FirstName = "Nishan1", LastName = "Dhungana1", Status = Status.Closed},
                new Name { FirstName = "Nishan2", LastName = "Dhungana2", Status = Status.New},
                new Name { FirstName = "Nishan3", LastName = "Dhungana3", Status = Status.New},
                new Name { FirstName = "Nishan4", LastName = "Dhungana4", Status = Status.Closed},
                new Name { FirstName = "Nishan5", LastName = "Dhungana5", Status = Status.Closed},
                new Name { FirstName = "Nishan6", LastName = "Dhungana6", Status = Status.InProgress},
                new Name { FirstName = "Nishan7", LastName = "Dhungana7", Status = Status.Closed},
                new Name { FirstName = "Nishan8", LastName = "Dhungana8", Status = Status.InProgress},
                new Name { FirstName = "Nishan9", LastName = "Dhungana9", Status = Status.InProgress},
                new Name { FirstName = "Nishan10", LastName = "Dhungana10", Status = Status.New},
                new Name { FirstName = "Nishan11", LastName = "Dhungana11", Status = Status.New}
            };

            list = list.OrderByDescending(x => x.Status == Status.New)
                .ThenByDescending(x => x.Status == Status.InProgress)
                .ThenByDescending(x => x.Status == Status.Closed)
                .ToList();

            Console.WriteLine("hey");
            Console.ReadLine();
        }
    }

    public class Name
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Status Status { get; set; }
    }

    public enum Status
    {
        New = 1,
        InProgress = 2,
        Closed = 3
    }
}

【讨论】:

  • 这是我所需要的。仅供参考,是否可以仅将其作为 GetAsync 方法的一部分包含在内?谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-23
相关资源
最近更新 更多