gpt4 book ai didi

c# - Entity Framework 扩展方法 ICollection/IQueryable

转载 作者:太空宇宙 更新时间:2023-11-03 19:50:18 25 4
gpt4 key购买 nike

我需要多次检查相同的特定条件(where 子句):

return _ctx.Projects.Where(p => p.CompanyId == companyId &&
(p.Type == Enums.ProjectType.Open ||
p.Invites.Any(i => i.InviteeId == userId))).ToList()

'&&'之后的部分将导致用户无法检索受限项目。

我想将此检查抽象为一个函数。将来这些条件可能会改变,我不想替换所有的 LINQ 查询。

我使用以下扩展方法做到了这一点:

public static IQueryable<Project> IsVisibleForResearcher(this IQueryable<Project> projects, string userId)
{
return projects.Where(p => p.Type == Enums.ProjectType.Open ||
p.Invites.Any(i => i.InviteeId == userId));
}

现在我可以将 LINQ 查询更改为:

return _ctx.Projects.Where(p => p.CompanyId == companyId)
.IsVisibleForResearcher(userId).ToList()

这会生成相同的 SQL 查询。现在,当我想在另一个具有项目的 DbSet 上使用此扩展方法时,我的问题就出现了。

假设一家公司有项目。而我只想检索用户至少可以看到一个项目的公司。

return _ctx.Companies
.Where(c => c.Projects.Where(p =>
p.Type == Enums.ProjectType.Open ||
p.Invites.Any(i => i.InviteeId == userId))
.Any())

这里我也喜欢用扩展的方式。

return _ctx.Companies
.Where(c => c.Projects.AsQueryable().IsVisibleForCompanyAccount(userId).Any())

这会抛出以下异常:

An exception of type 'System.NotSupportedException' occurred in Remotion.Linq.dll but was not handled in user code

Additional information: Could not parse expression 'c.Projects.AsQueryable()': This overload of the method 'System.Linq.Queryable.AsQueryable' is currently not supported.

我创建了以下扩展方法:

public static IEnumerable<Project> IsVisibleForResearcher(this ICollection<Project> projects, string userId)
{
return projects.Where(p => p.Type == Enums.ProjectType.Open ||
p.Invites.Any(i => i.InviteeId == userId));
}

但这也不起作用。

有人有想法吗?或朝着正确方向迈出的一步。

顺便说一句,我在 .NET Core 上使用 Entity Framework Core

更新:

使用 Expression<Func<>>导致相同的异常:

'System.Linq.Queryable.AsQueryable' is currently not supported.

更新 2

感谢@ivan-stoev 提供了解决方案。我还有一个问题。我还想检索“可见”项目的数量。

我通过这样做修复了它:

var companies = _ctx.Companies
.WhereAny(c => c.Projects, Project.IsProjectVisibleForResearcher(userId))
.Select(c => new CompanyListDto
{
Id = c.Id,
Name = c.Name,
LogoId = c.LogoId,
ProjectCount = _ctx.Projects.Where(p => p.CompanyId == c.Id)
.Count(Project.IsProjectVisibleForResearcher(userId))
});

但我找不到只使用 c.Projects 的方法而不是 ctx.Projects.Where(p => p.CompanyId == c.Id)

生成的 SQL 是正确的,但我想避免这种不必要的检查。

真诚的,布莱希特

最佳答案

IQueryable<T> 中使用表达式/自定义方法查询表达式一直存在问题,需要一些表达式树后处理。例如,LinqKit提供 AsExpandable , InvokeExpand为此目的自定义扩展方法。

虽然不那么笼统,但这里是不使用第 3 方包的示例用例的解决方案。

首先,提取一个方法中谓词的表达式部分。 IMO 的逻辑位置是 Project类:

public class Project
{
// ...
public static Expression<Func<Project, bool>> IsVisibleForResearcher(string userId)
{
return p => p.Type == Enums.ProjectType.Open ||
p.Invites.Any(i => i.InviteeId == userId);
}
}

然后,像这样创建一个自定义扩展方法:

public static class QueryableExtensions
{
public static IQueryable<T> WhereAny<T, E>(this IQueryable<T> source, Expression<Func<T, IEnumerable<E>>> elements, Expression<Func<E, bool>> predicate)
{
var body = Expression.Call(
typeof(Enumerable), "Any", new Type[] { typeof(E) },
elements.Body, predicate);
return source.Where(Expression.Lambda<Func<T, bool>>(body, elements.Parameters));
}
}

有了这个设计,就不需要你当前的扩展方法了,因为对于 Projects您可以使用的查询:

var projects = _ctx.Projects
.Where(p => p.CompanyId == companyId)
.Where(Project.IsVisibleForResearcher(userId));

Companies :

var companies = _ctx.Companies
.WhereAny(c => c.Projects, Project.IsVisibleForResearcher(userId));

更新:此解决方案非常有限,因此如果您有不同的用例(尤其是在第二次更新中的 Select 表达式中),您最好求助于一些第 3 方包.例如,这是 LinqKit 解决方案:

// LInqKit requires expressions to be put into variables
var projects = Linq.Expr((Company c) => c.Projects);
var projectFilter = Project.IsVisibleForResearcher(userId);
var companies = db.Companies.AsExpandable()
.Where(c => projects.Invoke(c).Any(p => projectFilter.Invoke(p)))
.Select(c => new CompanyListDto
{
Id = c.Id,
Name = c.Name,
LogoId = c.LogoId,
ProjectCount = projects.Invoke(c).Count(p => projectFilter.Invoke(p))
});

关于c# - Entity Framework 扩展方法 ICollection/IQueryable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40322424/

25 4 0