我有一个数据库结构,以处理人员(学生,工作人员,...),可以分配到不同的机构,为不同的日期。现在有一个查询,以获得所有工作人员的信息,如果单身人士目前是“活跃”或不是(意味着他们是该机构的一部分,在特定的一天,并没有离开产假)。因为“isActive”这个东西相当复杂,而且我不仅在这个查询中需要它,在其他地方也需要它,所以我创建了这个方法:
public static Func<StaffInstitute, bool> StaffIsActive(DateTime referenceDate)
{
return pe => (pe.EntryDate == null || pe.EntryDate <= referenceDate.Date) &&
(pe.ExitDate == null || pe.ExitDate >= referenceDate.Date) &&
(
!pe.Staff.MaternityLeave ||
(
pe.Staff.MaternityLeave &&
pe.Staff.MaternityLeaveFrom.HasValue &&
pe.Staff.MaternityLeaveUntil.HasValue &&
(pe.Staff.MaternityLeaveFrom.Value.Date > referenceDate.Date || pe.Staff.MaternityLeaveUntil.Value.Date < referenceDate.Date)
) ||
(
pe.Staff.MaternityLeave &&
(pe.Staff.MaternityLeaveFrom.HasValue || pe.Staff.MaternityLeaveUntil.HasValue) &&
(!pe.Staff.MaternityLeaveFrom.HasValue || pe.Staff.MaternityLeaveFrom.Value.Date >= referenceDate.Date) &&
(!pe.Staff.MaternityLeaveUntil.HasValue || pe.Staff.MaternityLeaveUntil.Value.Date < referenceDate.Date)
)
);
}
字符串
这样做的目的是为了方便地重用它,以便在将来的任何查询中,我都能够找到IsActive值,而不必复制这17行。因此,我的查询将获得所有员工的列表,如下所示:
Func<StaffInstitute, bool> staffIsActiveFunction = StaffIsActive(referenceDate);
IQueryable<StaffListDTO> staff =
from staffInstitute
in _context.StaffInstitute.Include(s => s.Staff)
select new StaffListDTO()
{
StaffId = staffInstitute.StaffId,
Name = staffInstitute.Staff.Name,
IsActive = staffIsActiveFunction.Invoke(staffInstitute),
};
型
这实际上很好用,这是一个令人愉快的惊喜。这个列表有一个分页,因为它得到得到相当长,所以在最后这将被添加到查询:
var res = staff.Skip(0).Take(30).ToList();
型
不幸的是,员工列表中有一个针对IsActive值的筛选器。有时候,我只想查看当前处于活动状态的员工,所以我在分页之前添加了以下内容:
if (istAktiv.HasValue)
{
staff = staff.Where(p => p.IsActive);
}
型
这就是它停止工作的地方,我得到以下错误:
The LINQ expression '...' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'
型
我也无计可施了。我不能在这一点上做.toList,因为这样查询将从数据库中获取所有条目,而不是仅30个。我能做些什么来使它工作?如果我把StaffIsActive的内容直接写入我的查询,它突然就起作用了,我只是不明白。为什么?为什么?
有什么办法吗?
2条答案
按热度按时间wfypjpf41#
您的查询需要表达式扩展。它可以由不同的库完成,几乎完整的列表可以找到here。我选择LINQKit:
使用
ExpandableAttribute
定义存根函数:字符串
定义返回Expression的私有实现。结果表达式的参数应模拟原始函数:
型
如果您使用EF Core,请在
DbContextOptions
构建期间激活LINQKit:型
对于EF6,使用
AsExpandable()
型
zaq34kh62#
我不确定你的EF版本,但从我最近在EF6的经验来看,即使是
staffIsActiveFunction.Invoke(staffInstitute)
也不应该工作。我想他们在新版本中修复了这个问题。无论如何,在EF6中,我使用
Expression<Func<T, bool>>
来处理这种情况。字符串
然后,我可以像这样在不同的地方应用相同的条件:
型
IQueryable<T>
有一个.Where(Expression<Func<T, bool>>)
过载。我想你可以:
1.重构
StaffIsActive
以返回Expression<Func<StaffInstitute, bool>>
。1.将其用于
IsActive
和.Where()
。当我们编写LINQ时,EF需要将语句转换为SQL查询。如果语句太复杂或不受EF支持,它将抛出。因此,在EF查询中调用自定义函数时应始终小心。一个通用的解决方案是使用如上所述的
Expression
s。