在代码中为导航属性项构建表达式树

问题描述 投票:0回答:1

我正在使用 VS2022 并使用 EF Core。我的数据上下文是 _myLab05Context,我有一个实体“解决方案”,它有一个导航属性“SolutionUsages”。 SolutionUsages 有一个属性“StudyId”。例如,我想检索 SolutionUsages.StudyId == 5 的所有解决方案。我可以使用以下代码执行此操作:

                var query = await _myLab05Context.Solutions
                .Select(s => new
                {
                    Solutions = s,
                    SolutionUsages = s.SolutionUsages.Where(su => su.StudyId == 5)
                })
                .Where(s => s.SolutionUsages.Any(su => su.StudyId == 5))
                .ToListAsync();

效果很好。作为动态方法的第一步,我生成了两个表达式:

            var anyQuery =
                _buildWhereExpression.GenericAny<Entities.Solution, Entities.SolutionUsage>("SolutionUsages",
                    "StudyId", 5L);

            var whereQuery =
                _buildWhereExpression.GenericWhere<Entities.Solution, Entities.SolutionUsage>("SolutionUsages",
                    "StudyId", 5L);

anyQuery 是 Expression> 类型,而 whereQuery 是 Expression> 类型,它们完全包含“手动”where-clauses 的语法。

    public Expression<Func<TSource, bool>> GenericAny<TSource, T2ndSource>(string navPropName, string propName, long propValue)
    {
        ParameterExpression pe02 = Expression.Parameter(typeof(T2ndSource), "x");
        MemberExpression me02 = Expression.Property(pe02, propName);
        ConstantExpression constant = Expression.Constant(propValue);
        BinaryExpression body = Expression.Equal(me02, constant);
        var funcExpression = Expression.Lambda<Func<T2ndSource, bool>>(body, pe02);


        ParameterExpression pe = Expression.Parameter(typeof(TSource), "s");
        MemberExpression me = Expression.Property(pe, navPropName);
        var navPropType = typeof(T2ndSource);
        var anyCall = Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new[] { navPropType }, me, funcExpression);
        var predicate = Expression.Lambda<Func<TSource, bool>>(anyCall, pe);

        return predicate;
    }

    public Expression<Func<T2ndSource, bool>> GenericWhere<TSource, T2ndSource>(string navPropName, string propName, long propValue)
    {
        ParameterExpression pe02 = Expression.Parameter(typeof(T2ndSource), "x");
        MemberExpression me02 = Expression.Property(pe02, propName);
        ConstantExpression constant = Expression.Constant(propValue);
        BinaryExpression body = Expression.Equal(me02, constant);
        var funcExpression = Expression.Lambda<Func<T2ndSource, bool>>(body, pe02);

        return funcExpression;
    }

(我知道 whereQuery 已经是 anyQuery 的一部分,但我将它们分开保存以分离错误。)

当我用“whereQuery”替换第一个 where-clause 时,我在“where”行中得到一个错误:

错误 CS1929“ICollection”不包含“Where”的定义,最佳扩展方法“Queryable.Where(IQueryable, Expression>)”的重载需要类型“System.Linq.IQueryable”的接收者。 (译自德语)。

当我用“anyQuery”替换第二个 where 子句时,出现错误

错误 CS1503 参数“2”:从“System.Linq.Expressions.Expression>”转换为“System.Linq.Expressions.Expression SolutionUsages>, int, bool>>”是不可能的。

如何修改表达式以便它们在查询中起作用?

c# linq expression-trees
1个回答
0
投票

我不完全确定我是否理解你正在尝试做的事情。以动态方式执行此操作可能会有点混乱。

我不确定我是否理解您的用例。但是,如果您的问题是应用表达式,那么最后一部分应该可以正常工作。

知道导航属性的类型时执行此操作的简单方法如下:

public static Expression<Func<TSource, TResult>> Combine<TSource, TProp, TResult>(
    Expression<Func<TSource, TProp>> selectExpression,
    Expression<Func<TProp, TResult>> transformExpression)
{
    var sourceParameter = propertyExpression.Parameters[0];
    var propertyAccess = propertyExpression.Body;
    var transformedAccess = Expression.Invoke(transformExpression, propertyAccess);
    return Expression.Lambda<Func<TSource, TResult>>(transformedAccess, sourceParameter);
}

public class Solution { public List<Study> SolutionUsages { get; set; } }
public class Study { public int StudyId { get; set; } }
public static void Example()
{
    IQueryable<Solution> solutions = new List<Solution>().AsQueryable();
    var exprWhereAny = ExprHelp.Combine<Solution, List<Study>, bool>(
        container => container.SolutionUsages,
        list => list.Any(s => s.StudyId == 5));

    var exprWhere = ExprHelp.Combine<Solution, List<Study>, IEnumerable<Study>>(
        container => container.SolutionUsages,
        list => list.Where(s => s.StudyId == 5))
        .Compile();

    var query = solutions
        .Where(exprWhereAny)
        .Select(s => new
        {
            Solutions = s,
            SolutionUsages = exprWhere.Invoke(s)
        })
        .ToList();
...
}

希望这至少能帮到你。

© www.soinside.com 2019 - 2024. All rights reserved.