开发者

LINQ: How to force a value based reference?

开发者 https://www.devze.com 2023-01-27 22:14 出处:网络
I want to provide a set of filters for a user to pick from, and each filter will correspond to an Expression<Func<X, bool>>.So, I might want to take a dynamic list of available items (\'Jo

I want to provide a set of filters for a user to pick from, and each filter will correspond to an Expression<Func<X, bool>>. So, I might want to take a dynamic list of available items ('Joe', 'Steve', 'Pete', etc), and create a collection of "hard-coded" filters based on those names, and let the user select which filter(s) he wants to use. My problem is that even when I try to "hard-code" my expression based on a string value from the dynamic list, the expression is still storing the value as, what looks to me, a property hanging off of an anonymous type (and I don'开发者_如何学JAVAt know how to serialize the anon. type). Sorry if this is confusing, I'm not quite sure how to articulate this.

Here's my sample code:

    public class Foo
    {
        public string Name { get; set; }
    }
    static void Main(string[] args)
    {
        Foo[] source = new Foo[]
            {
                new Foo() { Name = "Steven" } ,
                new Foo() { Name = "John" } ,
                new Foo() { Name = "Pete" },
            };

            List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>();
            foreach (Foo f in source)
            {
                Expression<Func<Foo, bool>> exp = x => x.Name == f.Name;
                filterLst.Add(exp);
            }
    }
}

My problem is that when I look at when I look at the body of my expression, it reads as follows:

(x.Name = value(ConsoleApplication1.Program+<>c__DisplayClass3).value)

When what I really want is for the first one to read like this:

(x.Name = "Steven")

(if I change my code to this, instead, that's exactly what I get:

      Expression<Func<Foo, bool>> exp = x => x.Name == "Steven";

)

I've tried forcing my value to a local string value before sticking it into the Expression, but it doesn't seem to help:

    List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>();
    foreach (Foo f in source)
    {
        string value = f.Name;
        Expression<Func<Foo, bool>> exp = x => x.Name == value;
        filterLst.Add(exp);
    }

I don't understand why (or really even how) it's still looking at some anonymous type even once I'm using a local variable that is declared to a string. Is there a way to make this work as I want it to?


The anon-type is actually the compiler-generated type it is using to perform capture of the variables. With delegates you can hack around this by implementing the capture by hand, but not with lambda expressions compiled to expression-trees.

Two choices:

  • build the expression tree explicitely on code via Expression.Constant etc
  • learn how to handle the anon types

The latter isn't too bad actually; they are just MemberExpression typically, although I have some code kicking around that covers this in full detail. I can also provide examples of buildin the expression tree, but I'm not at a PC at the moment and it doesn't lend itself well to iPod typing...

From a brief read of the question I'd look at the first option more than the second.

Oh, and watch out; the first foreach code in the question looks susceptible to the notorious l-value capture issue ;)


Edit: I found a PC ;p

        var param = Expression.Parameter(typeof(Foo), "x");
        var body = Expression.Equal(
            Expression.PropertyOrField(param, "Name"),
            Expression.Constant(f.Name, typeof(string)));

        var exp = Expression.Lambda<Func<Foo, bool>>(body, param);
        filterLst.Add(exp);


Marc Gravell's answer is correct, and here's how you'd implement his first choice:

var filters = 
    from f in source
    let param = Expression.Parameter(typeof(Foo),"x")
    select Expression.Lambda<Func<Foo, bool>>(
        Expression.Equal(
            Expression.Property(param, "Name"),
            Expression.Constant(f.Name)), param);

But this typically isn't necessary. Your second example of the for loop should work with all major LINQ providers. Is there a reason you need the expression to use constants?

0

精彩评论

暂无评论...
验证码 换一张
取 消