实体框架共享查询



我不太知道如何措辞,所以我只是要解释我的场景。

我有一个方案,即EmploymentHistory表上的TerminationDate字段可以是 null,也可以是将来的日期。EmploymentHistory联接到Employees表,这是一个 1:M 关系,其中单个Employee可以有多个EmploymentHistory记录。 该Employees表连接到许多不同的位置,例如表示Employees可以登录的前端门户的Users表。

我经常只需要抓住活跃的员工。 处于活动状态的 SQL 逻辑是WHERE TerminationDate IS NULL OR TerminationDate >= GETDATE()。 因此,如果终止日期为空或将来设置

。所以在 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>>但除了我认为我有一个解决方案,如果我可以让所有需要这个的实体都从基地继承,并且总是可以假设有一个叫做员工的导航道具坐在那里,基地是我的T。 如果我尝试扩展方法,EF Core 只能评估它在客户端,而不是服务器端。

你可以为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();.

这不是代码的巨大减少,而是少了很多类型,如果您出于某种原因需要更改逻辑并且不想在任何地方都显示它,那就更好了。

有许多方法可以做到这一点。 使用 SQL Server 和 EF,您可以

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

我在旧版本的 EF 中尝试了选项 1,并且花了很长时间才完成它。 性能不是很好,我再也没有尝试过。 不过,看起来这在较新版本的 EF 中变得更容易了,因此欢迎您尝试一下。

选项 2 可能提供了性能和准确性的最佳平衡,因为您可以优化视图的查询,然后仍然贪婪地加载数据以获得最佳效率。

选项3 的工作方式与选项 2 非常相似,并且是最简单的 C# 代码,但它的效率不会很高,因为它必须单独处理列而不是聚合处理列。

有时我发现您必须使用物化信息,并且它确实提供了绝对最佳的性能,因为在运行时没有添加计算。 但是,您可能会面临数据不同步的风险,因此我只会将其保留在性能至关重要的最严重情况下。

@Rounder在这里对@WeskerTyrant答案的评论中引用的错误现在已经在 EF Core 中得到解决。

因此,您可以执行以下操作:

Expression<Func<T, bool>> IsActiveEmployee =
eh => eh.TerminationDate == null ||
eh.TerminationDate >= DateTime.Today

然后这样称呼它:

context.Users.AsQueryable().Where(IsActiveEmployee).ToList();

请注意添加的.AsQueryable()允许.Where()接受Expression

最新更新