基于导航属性的任意方法的泛型表达式

问题描述

我正在尝试编写一个基于通用表达式的方法,用于在 DbSet 的导航属性添加“Any”查询。我正在使用 dotnet core 5 和实体框架核心。目前我得到以下信息:

'DbSet().Where(u => u.UserRoles.Any())' 无法翻译

这对我来说似乎是一个合理的查询,尽管我不确定所有类型是否正确,或者这是否可能。我目前必须构建的方法如下:

private IQueryable<TEntity> GenericAny(IQueryable<TEntity> queryable)
{
    // attempting to replicate
    // users.Where(u => u.UserRoles.Any());
    var linkedEntityName = "UserRoles";
    // once working look at passing these in as parameters

    Type listType = typeof(TEntity).GetProperty(linkedEntityName).PropertyType.GetGenericArguments()[0];

    var param = Expression.Parameter(typeof(TEntity),"u");
    MemberExpression body = Expression.Property(param,linkedEntityName);

    // find AsQueryable method to be called on navigation prop
    var toQueryable = typeof(Queryable).getmethods()
            .Where(m => m.Name == "AsQueryable")
            .Single(m => m.IsGenericmethod)
            .MakeGenericmethod(listType);

    var anyLambda = Expression.Lambda<Func<TEntity,bool>>(
                            Expression.Call(
                                typeof(Enumerable),"Any",new Type[] { listType },Expression.Call(null,toQueryable,body)),Expression.Parameter(typeof(TEntity),"u"));

    var whereMethod = typeof(Queryable)
                            .getmethods()
                            .Where(m => m.Name == "Where" && m.GetParameters().Length == 2)
                            .First()
                            .MakeGenericmethod(typeof(TEntity));

    var whereCallExpression = Expression.Call(
                                    whereMethod,queryable.Expression,anyLambda);

    return queryable = queryable.Provider.createquery<TEntity>(whereCallExpression);
}  

UserRoles 属性在 User 对象上定义为:

[InverseProperty(nameof(UserRole.User))]
public virtual ICollection<UserRole> UserRoles { get; set; }

我可以使用以下命令直接调用 dbset:

var query =  _context.Users
                .Include(u => u.UserRoles)
                .AsQueryable();

return query.Where(u => u.UserRoles.Any());

在这里遗漏了什么吗?非常感谢任何帮助。

解决方法

这是正确的实现,没有不需要的操作。

public static IQueryable<TEntity> GenericAny<TEntity>(this IQueryable<TEntity> queryable,string linkedEntityName)
{
    var param = Expression.Parameter(typeof(TEntity),"e");
    var propExpression = Expression.Property(param,linkedEntityName);
    var listType = propExpression.Type.GetGenericArguments()[0];

    var anyCall =
        Expression.Call(
            typeof(Enumerable),nameof(Enumerable.Any),new[] { listType },propExpression
        );

    var predicate = Expression.Lambda<Func<TEntity,bool>>(anyCall,param);

    return queryable.Where(predicate);
}