从 IQueryable<T> 谓词得到 Func<T, bool>

Get from IQueryable<T> predicate Func<T, bool>

我正在尝试将查询从一个集合应用到另一个集合。 我的测试样本:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        Expression sourceExpression = Amazings().Where(x => x.Name.Equals("Kasa")).AsQueryable().Expression;

        Expression<Func<Amazing, bool>> predicate = Expression.Lambda<Func<Amazing, bool>>(sourceExpression, Expression.Parameter(typeof(Amazing)));

        var col2 = Amazings().Where(predicate.Compile());
    }

    private IEnumerable<Amazing> Amazings()
    {
        var amazings = new List<Amazing>
            {
                new Amazing
                    {
                        Name = "Kasa",
                    },
                new Amazing
                    {
                        Name = "Ma@p2a"
                    }
            };
        return amazings;
    }

    class Amazing
    {
        public string Name { get; set; }
    }
}

怎么了? 当我在调试中 运行 这个测试时,我得到了异常:

您不能对 return 类型“System.Boolean”使用“System.Linq.EnumerableQuery`1[UnitTestProject1.UnitTest1+Amazing]”等表达式。

如果我运行这个没有参数我得到异常我没有参数。

如果我将参数更改为 boolean 类型,我会遇到异常:您不能将元素 bool 类型用于委托参数类型 Amazing...

我测试了几个选项,但没有任何效果。

我还发现密钥可以通过将 Expression 强制转换为:MethodCallExpression 但它不是,或者我不知道如何。

我试过这样的东西:

MethodCallExpression e = query.Expression as MethodCallExpression;
        MemberExpression memberExpression = (MemberExpression)e.Object;

        Expression<Func<Amazing, bool>> getCallerExpression = Expression<Func<Amazing>>.Lambda<Func<Amazing, bool>>(memberExpression);

很遗憾,memberExpression 为空。

我在网上搜索了一下,唯一找到的是提示:~"don't do this".

我怎样才能实现我的目标?

由于您调用的是 Enumerable.Where 而不是 Queryable.Where,因此您拥有的 lambda 从未被翻译成 Expression,它只会被编译成一个方法,而 IQueryable 你甚至没有任何办法到达那个代表,因为 看到的只是一个任意的 IEnumerable<T>.

不清楚您想做什么...但是正如 Servy 所写,您将 AsQueryable() 放在了错误的位置。正确的地方是 before the .Where(),这是一个单元测试,我建议 returns 数据的方法应该直接 return IQueryable<T>,以更好地模拟您可以在代码中找到的 "normal" 查询。

试试看看这是不是你想要的...

public void TestMethod1()
{
    var q1 = Amazings().Where(x => x.Name.Equals("Kasa"));

    Expression predicate = q1.Expression;

    var q2 = Amazings();
    IQueryable<Amazing> q3 = q2.Provider.CreateQuery<Amazing>(predicate);
}

private IQueryable<Amazing> Amazings()
{
    var amazings = new List<Amazing>
    {
        new Amazing
            {
                Name = "Kasa",
            },
        new Amazing
            {
                Name = "Ma@p2a"
            }
    };
    return amazings.AsQueryable();
}

请注意,在大多数情况下,您不能简单地移动 .Where()Expression<Func<Amazing, bool>> 条件,因为您可能有 .Where().Select() 或类似的东西。

其他可能的解决方案,如果您知道最后一个方法是 .Where() 并且您 真的 想要 "move" Expression<Func<Amazing, bool>>

MethodCallExpression mce = predicate as MethodCallExpression;

if (mce == null)
{
    throw new Exception();
}

UnaryExpression quote = mce.Arguments[1] as UnaryExpression;

if (quote == null || quote.NodeType != ExpressionType.Quote)
{
    throw new Exception();
}

Expression<Func<Amazing, bool>> lambda = quote.Operand as Expression<Func<Amazing, bool>>;

if (lambda == null)
{
    throw new Exception();
}

IQueryable<Amazing> q4 = Amazings().Where(lambda);