首先,对不起我的英语,并感谢您阅读我的问题。
我将EF Core 5.0.1和ASP NET 5.0.1 Web API一起PredicateBuilder
使用,并且我想使用来构建查询LinqKit.Microsoft.EntityFrameworkCore 5.0.2.1
为了这个问题,我将模型简化为:
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
建立了适当关系的类。
所有我想要实现的是搜索任何User
属于任何Departament
这Department.Name
是一个List<String>
。
在List<String>
包含关键字的列表,没有确切的部门名称
Department
表具有这种行:
Id部门 | 名称 |
---|---|
1个 | 行政 |
2 | HRHRR |
3 | 营业额 |
4 | 营销学 |
并且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);
这将返回我期望的匹配,但也有两个问题。
由于EF CoreINNER JOIN
为每个关键字添加,SQL转换的性能很差。无论kwDepartment
具有两个或三个元素,但具有100或1000个元素都是不允许的。
每个INNER JOIN
在Departments
表上都有一个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
运算符不是强制性的,但是为了更好的理解,我把它放在了那里。
再次感谢!
您可以使用Or
谓词走上正确的道路,但您应该创建一个基于单个谓词的内部谓词,以代替在该谓词中使用多个Or
谓词。user.UserDepatments.Any(single_match)
Or
user.UserDepatments.Any(multi_or_match)
像这样:
var departtmentPredicate = kwDepartments
.Select(kw => Linq.Expr((Department d) => EF.Functions.Like(d.Name, "%" + kw + "%")))
.Aggregate(PredicateBuilder.Or);
接着
predicate = predicate.And(u => u.UserDepartments
.Select(ud => ud.Department) // navigate to department
.AsQueryable() // to be able to use departtmentPredicate expression directly
.Any(departtmentPredicate));
使用该代码和示例列表,DbSet<User>().Where(predicate)
将其翻译成如下形式:
DECLARE @__p_1 nvarchar(4000) = N'%mark%';
DECLARE @__p_2 nvarchar(4000) = N'%admin%';
SELECT [u].[IdUser], [u].[Name]
FROM [User] AS [u]
WHERE EXISTS (
SELECT 1
FROM [UserDepartment] AS [u0]
INNER JOIN [Department] AS [d] ON [u0].[IdDepartment] = [d].[IdDepartament]
WHERE ([u].[IdUser] = [u0].[IdUser]) AND (([d].[Name] LIKE @__p_1) OR ([d].[Name] LIKE @__p_2)))
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句