Get from IQueryable<t> predicate Func<t, bool="">

时间:2015-06-25 18:23:08

标签: c# linq expression iqueryable func

I'm trying apply query from one collection to another. Sample of my test:

[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; }
    }
}

What is wrong? When I run this test in debug I get the exception:

You can not use expressions such as „System.Linq.EnumerableQuery`1[UnitTestProject1.UnitTest1+Amazing]” for the return type „System.Boolean”.

If I run this without parameter I got exception that I don't have parameters.

If I change parameter to boolean type I got exception: you can't use element bool type for delegate parameter type Amazing...

I tested few more options but nothing works.

Also I found that the key may by casting Expression to: MethodCallExpression but it's not or I can't figurate how.

I tried with somothing like this:

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

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

Unfortunately memberExpression is null.

I searched the web and the only thing I found it's a tip: ~"don't do this".

How can I achieve my goal?

2 个答案:

答案 0 :(得分:3)

Since you're calling Enumerable.Where and not Queryable.Where the lambda you have is never translated into an Expression, it's only ever compiled into a method, and the IQueryable that you have doesn't even have any way of getting to that delegate, because all that it sees is an arbitrary IEnumerable<T>.

答案 1 :(得分:2)

It isn't clear what you want to do... But as Servy wrote, you put the AsQueryable() in the wrong place. The right place is before the .Where(), and this being a Unit Test, I suggest that the method that returns the data should directly return a IQueryable<T>, to better simulate "normal" queries you can find in code. Try looking if this is what you want... 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(); } Note that in the most general case, you can't simply move the Expression<Func<Amazing, bool>> condition of a .Where(), because you could have a .Where().Select() or something similar. Other possible solution, if you know that the last method is a .Where() and you really want to "move" the 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);