如何找到传递给
Expression
的
IQueryExpressionInterceptor.QueryCompilationStarting
引用的所有实体类型?我应该在表达式树的哪里寻找它们?
我尝试了各种查询并检查了调试器中的
Expression
。查询中涉及的实体类型似乎总是出现在表达式及其递归 Type
的 Arguments
属性中,作为其本身或作为 IQueryable
或 IIncludableQueryable
的类型参数。但我想知道是否有方法可以构造一个 EF 查询,但事实并非如此。
以下是我测试过的一些查询形状。在每种情况下,我都会看到一系列嵌套的
MethodCallExpression
(LINQ 类型)包围着 EntityQueryRootExpression
(EF 类型)。
var first = await db.Foos.FirstAsync();
var name = await db.Foos.Select(f => f.Name).FirstAsync();
var foosWithBars = await db.Foos.Include(f => f.Bars).ToListAsync();
var bars = await db.Foos
.Where(f => f.FooId == 1)
.SelectMany(f => f.Bars)
.ToListAsync();
var firstBar = await db.Foos
.OrderBy(f => f.FooId)
.Select(f => f.Bars.First())
.FirstAsync();
考虑仅关注
QueryRootExpression
和 MemberExpression
,因为它们包含有关 LINQ 查询涉及哪些实体的足够信息。
下面是一个简单的
ExpressionVisitor
实现,它收集了这些实体类型:
用途:
var visitor = new EntitiesCollectorVisitor(db.Model);
visitor.Visit(queryExpression);
foreach (var entityType in visitor.Entities)
{
Console.WriteLine(entityType.ClrType.Name);
}
实施:
public class EntitiesCollectorVisitor : ExpressionVisitor
{
private readonly IModel _model;
public HashSet<IEntityType> Entities { get; } = new();
public EntitiesCollectorVisitor(IModel model)
{
_model = model;
}
void CollectTypes(Type type)
{
var entityType = _model.FindEntityType(type);
if (entityType != null)
{
Entities.Add(entityType);
}
else if (type.IsGenericType)
{
foreach (var genericArg in type.GenericTypeArguments)
CollectTypes(genericArg);
}
}
protected override Expression VisitMember(MemberExpression node)
{
CollectTypes(node.Type);
return base.VisitMember(node);
}
protected override Expression VisitExtension(Expression node)
{
if (node is QueryRootExpression rootExpression)
{
CollectTypes(rootExpression.ElementType);
}
return base.VisitExtension(node);
}
}