C# 实体框架共享查询

C# 实体框架共享查询,c#,entity-framework,ef-core-2.0,C#,Entity Framework,Ef Core 2.0,我不知道该怎么说,所以我只想解释一下我的情况 我有一个场景,在EmploymentHistory表上有一个终止日期字段,该字段可以为空,也可以是将来的日期EmploymentHistory连接到一个Employees表,它是一个1:M关系,其中单个Employees可以有多个EmploymentHistory记录。Employees表连接到许多不同的位置,例如Users表,该表表示Employees可以登录的前端门户 我经常只需要抓取活跃的员工。处于活动状态的SQL逻辑是,其中Terminati

我不知道该怎么说,所以我只想解释一下我的情况

我有一个场景,在
EmploymentHistory
表上有一个
终止日期
字段,该字段可以为空,也可以是将来的日期
EmploymentHistory
连接到一个
Employees
表,它是一个1:M关系,其中单个
Employees
可以有多个
EmploymentHistory
记录。
Employees
表连接到许多不同的位置,例如
Users
表,该表表示
Employees
可以登录的前端门户

我经常只需要抓取活跃的员工。处于活动状态的SQL逻辑是
,其中TerminationDate为NULL或TerminationDate>=GETDATE()
。因此,如果TerminationDate为null,或者它是在将来设置的

因此,在EF中,我有如下查询:

Expression<Func<T, bool>> IsActiveEmployee =
    eh => eh.TerminationDate == null ||
          eh.TerminationDate >= DateTime.Today
//抓取所有活动员工
context.Employees.Where(e=>e.EmploymentHistory.Any(eh=>eh.TerminationDate==null | | eh.TerminationDate>=DateTime.Today)。ToList();

//获取所有用户
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。今天
基本导航道具可能不同时的部分逻辑?尝试使用
表达式
,但除了我认为我有一个解决方案外,其他地方都没有,如果我可以使所有需要该属性的实体从一个基础继承,并且可以始终假设有一个导航道具ca坐在那里的是我的
T
。如果我尝试扩展方法,EF Core只能在客户端计算,而不能在服务器端计算。

你可以为
员工历史记录编写一个表达式:
表达式activeEmployee=eh=>eh.TerminationDate==null | | eh.TerminationDate>=DateTime.Today;

这样使用:
context.Employees.Where(e=>e.EmploymentHistory.Any(activeemployees)).ToList();


context.Users.Where(u=>u.Employee.EmploymentHistory.Any(activeEmployee)).ToList();


这并不是大幅减少代码,而是大大减少了键入,如果您出于某种原因需要更改逻辑,并且不想在任何地方更改逻辑,情况会更好。

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

  • 在SQL中创建一个函数并将其导入EF。这有点棘手,但可以做到
  • 在执行计算的服务器上创建一个视图,并在EF中定义关系,以便可以从该视图调用并从中加载所需的信息
  • 向表中添加计算列以显示用户是否处于活动状态
  • 向表中添加物化列以跟踪用户是否处于活动状态,并在现有数据之外维护该列
  • 我在旧版本的EF中尝试了选项1,但它花了很长时间才完成。性能不太好,我再也没有尝试过。不过,在新版本的EF中,这似乎变得更容易了,所以欢迎您尝试一下

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

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


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

    h中@Rounder引用的错误关于@weskertrant的答案的评论现在已经在efcore中了

    所以,你可以这样做:

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

    注意添加了
    .AsQueryable()
    ,它允许
    .Where()
    要接受一个
    表达式

    您有两个选项,存储表达式、基本表达式并根据您调用的表达式替换输入参数。或者,您也可以只存储IQueryable to employees,当您想让用户调用employees queryable,然后添加selectMany(e=>e.Users)谢谢你的回复Wesker。这是我一直在寻找的东西。不幸的是,它看起来不适合EF Core。EF Core需要一个Func,然后当我这样做时,我得到了一个NotSupportedException:给定的参数与预期的参数不匹配:类型为'System.Linq.Expressions.TypedParameterExpression'的对象无法b转换为“System.Linq.Expressions.LambdaExpression”类型。看起来这是EF核心中的一个bug-