实体框架核心-按计算字段排序

时间:2020-10-19 21:23:03

标签: c# entity-framework linq asp.net-core .net-core

我有一个与此相似的模型:

Public class Item {
  public int Id { get; set; }
  public string Name { get; set; }
  public DateTime? StartTime { get; set; }
  public DateTime? EndTime { get; set; }
  public int? TotalTime
  {
    get
    {
      return Convert.ToInt32(EndTime.Value - StartTime.Value).TotalMinutes);
    }
  }
  etc...

然后我得到了一个物品清单:

IQueryable<Items> items = itemsContext.Items.Where(e => e.Id > 0);

我进行了一些计算,运行了一些其他代码,然后最后我想返回按名称ASC和TotalTime DESC排序的项目列表。这是我尝试过的:

items = items.OrderBy(e => e.Name).ThenByDescending(e => e.TotalTime);

但是我收到一个错误:“ LINQ表达式无法翻译。要么以可以翻译的形式重写查询,要么通过插入对AsEnumerable(),AsAsyncEnumerable的调用来显式切换到客户端评估(),ToList()或ToListAsync()。”

我也尝试过此操作,但收到相同的错误:

items = items.OrderBy(e => e.Name).ThenByDescending(e => e.TotalTime ?? 0);

如何按TotalTime对数据集进行排序?

1 个答案:

答案 0 :(得分:1)

  • 由于TotalTime不是数据库的一部分,因此您需要使用[NotMapped]对其进行注释。
  • 然后,您需要先加载项目,然后然后对它们进行排序。
  • 但是,如果要在数据库服务器中执行分页或排序,则需要在查询中直接使用EndTimeStartTime

方法1:首先加载,然后按应用程序代码排序:

List<Items> loadedItems = await itemsContext.Items
    .Where( e => e.Id > 123 )
    .ToListAsync()
    .ConfigureAwait(false); // `ConfigureAwait(false)` MAY be optional, depending on your application.

List<Items> sortedItems = loadedItems
    .OrderBy(e => e.Name)
    .ThenByDescending(e => e.TotalTime)
    .ToList(); // <-- Note this is NOT `ToListAsync` because the items are already loaded into memory.

方法2:以SQL排序:

List<Items> loadedSortedItems = await itemsContext.Items
    .Where( e => e.Id > 123 )
    .OrderBy( e => e.Name )
    .ThenByDescending( e => e.EndTime - e.StartTime )
    .ToListAsync()
    .ConfigureAwait(false);

更改您的班级

我将您的班级修改为如下形式:

public class Item {
  public int       Id        { get; set; }
  public string    Name      { get; set; }
  public DateTime? StartTime { get; set; }
  public DateTime? EndTime   { get; set; }
  
  [NotMapped]
  public int? TotalTimeMinutes
  {
    get
    {
        if( this.EndTime == null || this.StartTime == null ) return null;

        TimeSpan diff = this.EndTime.Value - this.StartTime.Value;
        return (Int32)Math.Round( diff.TotalMinutes );
    }
  }
  • NotMapped属性使Entity Framework清楚应该忽略该属性。
  • 请注意,该属性如何包含单位名称(“分钟”),否则,如果该属性是毫秒,秒,分钟等,则不会很明显。
  • 还要注意,属性如何检查StartTimeEndTime是否为空,并在这种情况下返回null。您的代码没有,这意味着如果数据库中有NULL个值或没有完全初始化任何Item个对象实例,则程序将崩溃。
  • 如果无法将StartTimeEndTime的值 设为NULL,则需要向数据库中添加NOT NULL并更新EF实体类,以便属性是DateTime而不是DateTime?