Entity Framework Core - 计算字段排序
Posted
技术标签:
【中文标题】Entity Framework Core - 计算字段排序【英文标题】:Entity Framework Core - Sorting on Calculated Field 【发布时间】:2021-02-02 16:43:28 【问题描述】:我有一个类似的模型:
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);
我做了一些计算,运行了一些其他代码,最后我想返回按 Name ASC 和 TotalTime DESC 排序的 Items 列表。这是我尝试过的:
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 对我的数据集进行排序?
【问题讨论】:
不要那样做。 TotalTime 属性无法计算一个服务器,因为 EF 看不到表达式树,只是属性调用。隐藏在属性 getter 下的代码。所以 EF 不能翻译这个表达式, 顺便说一句,避免使用Convert.ToInt32
,因为它不能让您控制错误处理。您可以安全地将TimeSpan.TotalMinutes
直接转换为Int32
。
对于此类记录内计算,将计算列添加到数据库并将其映射到计算后的 EF 模型始终是最佳选择。
【参考方案1】:
由于TotalTime
不属于您的数据库,因此您需要使用[NotMapped]
对其进行注释。
那么您需要先加载项目并然后按它们排序。
但是,如果您想在数据库服务器中执行分页或排序,则需要在查询中直接使用 EndTime
和 StartTime
。
方法一:先加载,再在应用代码中排序:
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.
方法二:在 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 表明它应该忽略该属性。
注意属性如何包含单位名称(“分钟”),否则如果属性是毫秒、秒、分钟等,则不明显。
还要注意属性如何检查StartTime
和EndTime
是否为空并在这种情况下返回null
。您的代码没有,这意味着如果数据库中有 NULL
值或任何 Item
对象实例未完全初始化,您的程序将崩溃。
如果StartTime
和EndTime
的值不能为NULL
,那么您需要将NOT NULL
添加到您的数据库并更新您的EF 实体类,以便属性为DateTime
而不是DateTime?
。
【讨论】:
好的。谢谢。如果您没有 getter 和 setter 两者,则 [NotMapped] 不是必需的另外,感谢有关时间计算的说明。我的意思是写一个简单的例子,它很容易理解并且代表了一个更复杂的情况。我通过在尝试排序之前先调用 ToList() 解决了我的问题。 第二种方法不正确 @HamedHajiloo 请解释为什么你觉得它不正确。【参考方案2】:您应该将 SQL DateDiff 方法转换为 EF Core。 对于这种方法,您可以使用以下方法。
EF.Functions.DateDiffMinute(c.StartDate, c.EndDate ?? DateTime.Now)
【讨论】:
以上是关于Entity Framework Core - 计算字段排序的主要内容,如果未能解决你的问题,请参考以下文章
EF6 System.Data.Entity.Core.EntityKey 在 Entity Framework Core 中的等价物是啥?
张高兴的 Entity Framework Core 即学即用:创建第一个 EF Core 应用