我不太清楚怎么说这个,所以我只想解释一下我的情景。
我有一个场景,TerminationDate
表上的EmploymentHistory
字段可以为null,或者是将来的日期。 EmploymentHistory
加入Employees
表,它是一个1:M的关系,其中一个Employee
可以有多个EmploymentHistory
个记录。该Employees
表连接到许多不同的位置,例如表示Users
可以登录的前端门户的Employees
表。
我经常需要只抓住活跃的员工。用于激活的SQL逻辑是WHERE TerminationDate IS NULL OR TerminationDate >= GETDATE()
。因此,如果TerminationDate为null或将来设置它。
所以在EF中我有这样的查询:
// Grab all Active Employees
context.Employees.Where(e => e.EmploymentHistory.Any(eh => eh.TerminationDate == null || eh.TerminationDate >= DateTime.Today).ToList();
// Get all Users
context.Users.Where(u => u.Employee.EmploymentHistory.Any(eh => eh.TerminationDate == null || eh.TerminationDate >= DateTime.Today).ToList();
等
这个逻辑出现在大约5个不同的地方。当基本导航道具可能不同时,如何共享逻辑的EmploymentHistory.Any(eh => eh.TerminationDate == null || eh.TerminationDate >= DateTime.Today
部分?尝试玩Expression<Func<T, bool>>
,但除了我认为我有一个解决方案,如果我可以让所有需要这个继承自Base的实体,并且总是假设有一个名为Employee的导航道具坐在那里而基地是我的T
。如果我尝试扩展方法,EF Core只能评估它的客户端,而不是服务器端。
答案 0 :(得分:3)
您可以为EmploymentHistory
:Expression<Func<EmploymentHistory, bool>> activeEmployee = eh => eh.TerminationDate == null || eh.TerminationDate >= DateTime.Today;
并像这样使用它:
context.Employees.Where(e => e.EmploymentHistory.Any(activeEmployee)).ToList();
和
context.Users.Where(u => u.Employee.EmploymentHistory.Any(activeEmployee)).ToList();
。
这不是代码的大幅减少,而是打字的次数要少得多,如果你因为某种原因需要改变逻辑并且不想在任何地方改变它,那就更好了。
答案 1 :(得分:1)
有很多方法可以做到这一点。使用SQL Server和EF可以
我在旧版本的EF中尝试了选项1,并且需要花费很长时间才能完成它。表现不是很好,我再也没试过。看起来这在新版本的EF中变得更容易了,所以欢迎你试一试。
选项2可能提供最佳的性能和准确性平衡,因为您可以优化视图查询,然后仍然贪婪地加载数据以获得最佳效率。
选项3的工作方式与选项2非常相似,并且是最简单的C#代码,但它不会那么高效,因为它必须单独处理列而不是聚合。
有些时候我发现你必须使用物化信息,它确实提供了绝对最佳的性能,因为在运行时没有额外的计算。您可能会冒着数据不同步的风险,因此我会将其保留用于性能至关重要的最严重情况。
答案 2 :(得分:0)
@Rounder在对@WeskerTyrant的答案here的评论中引用的错误现在已在EF Core中成为resolved。
因此,您可以执行以下操作:
Expression<Func<T, bool>> IsActiveEmployee =
eh => eh.TerminationDate == null ||
eh.TerminationDate >= DateTime.Today
然后像这样调用它:
context.Users.AsQueryable().Where(IsActiveEmployee).ToList();
请注意,添加了.AsQueryable()
,使.Where()
可以接受Expression
。