实体框架共享查询

时间:2018-01-04 23:05:52

标签: c# entity-framework ef-core-2.0

我不太清楚怎么说这个,所以我只想解释一下我的情景。

我有一个场景,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只能评估它的客户端,而不是服务器端。

3 个答案:

答案 0 :(得分:3)

您可以为EmploymentHistoryExpression<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可以

  1. 在SQL中创建一个函数并将其导入EF。这有点棘手,但可以做到。
  2. 在执行计算的服务器上创建一个视图,并在EF中定义关系,以便您可以从视图中调用并贪婪地从那里加载所需的信息。
  3. 向表中添加计算列以显示用户是否处于活动状态。
  4. 向表格添加实体化列,以跟踪用户是否处于活动状态,并在现有数据之外维护该列。
  5. 我在旧版本的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