【发布时间】:2021-03-29 20:42:26
【问题描述】:
我正在使用带有 ASP NET 5.0.1 Web API 的 EF Core 5.0.1,我想使用 LinqKit.Microsoft.EntityFrameworkCore 5.0.2.1 构建带有 PredicateBuilder 的查询
出于问题的目的,我将模型简化为:
public class User
{
public long IdUser { get; set; }
public string Name { get; set; }
public virtual ICollection<UserDepartment> UserDepartments { get; set; }
}
public class Department
{
public long IdDepartament { get; set; }
public string Name { get; set; }
public virtual ICollection<UserDepartment> UsersDepartment { get; set; }
}
public UserDepartment
{
public long IdUser { get; set; }
public long IdDepartment { get; set; }
public virtual Department { get; set; }
public virtual User { get; set; }
}
一个User 可以有多个Departaments,一个Departament 可以有多个Users
三个模型在 SQL Server 中都有对应的表和一个 IEntityTypeConfiguration 类,并设置了适当的关系。
我想要实现的只是搜索属于任何Departament 的任何User,其中Department.Name 在List<String> 中。
List<String> 包含关键字列表,而不是确切的部门名称
Department 表有这种行:
| IdDepartment | Name |
|---|---|
| 1 | Administration |
| 2 | HHRR |
| 3 | Sales |
| 4 | Marketing |
List<String> 可以是任何关键字,如“Admin”、“Sal”、“Mark”等。
第一次尝试
... 是建立一个这样的谓词:
List<string> kwDepartments = new List<String> {"mark","admin"};
var predicate = PredicateBuilder.New<User>(true);
predicate = predicate.And(x => x.UserDepartments.Where(y => kwDepartments.Any(c => y.Department.Name.Equals(c))).Any());
这会产生一个带有 IN 运算符的 SQL,如下所示:
...[t].[Name] IN (N'mark', N'admin'))
显然这不是我想要的,但如果我使用.Contains 而不是.Equals,则会引发异常
无法翻译 LINQ 表达式
我认为这是因为我正在尝试评估非原始值。
第二次尝试
... 是迭代 kwDepartments 并为每个字符串添加一个 .Or,如下所示:
foreach (string dep in kwDepartments )
{
predicateDep = predicateDep.Or(x => x.UserDepartments.Where(y=> y.Department.Name.Contains(dep)).Any());
}
predicate = predicate.And(predicateDep);
这会返回我期望的匹配,但也有两个问题。
-
SQL 转换性能不佳,因为 EF Core 为每个关键字设置了
INNER JOIN。不管kwDepartment有两个或三个元素,但100个或1000个元素都是不允许的。 -
Departments表上的每个INNER JOIN都有一个SELECT包含所有表字段,我不需要它们。我尝试在谓词中添加.Select(x=> x.Name)语句以仅使用两个字段,但没有效果。
第三次尝试
... 正在使用 EF.Functions.FreeText 进行全文搜索,但似乎没有任何区别。
我的目标是建立一个谓词,翻译成类似于:
SELECT [c.IdUser]. [c.Name]
FROM [User] AS [c]
WHERE EXISTS (
SELECT 1
FROM [UserDepartment] AS [u]
INNER JOIN (
SELECT [c0].[IdDepartment], [c0].[Name] <--ONLY NEED TWO FIELDS INSTEAD OF ALL FIELDS
FROM [Department] AS [c0]
) AS [t] ON [u].[IdDepartment] = [t].[IdDepartment]
WHERE ([c].[IdUser] = [u].[IdUser]) AND ([t].[Name] like (N'admin%') or [t].[Name] like (N'mark%')))
LIKE 运算符不是强制性的,但我放在那里是为了更好地理解。
再次感谢!
【问题讨论】:
标签: c# sql-server linq asp.net-web-api entity-framework-core