开发者

c# Lambda Expression built with LinqKit does not compile

This lambda does not compile, but I do not understand why.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using LinqKit;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
    {
        var barModel = new BarModel();
        string id = "some";

        Console.WriteLine(barModel.subFor(id).ToString());
            // output: m => (True AndAlso (m.key == value(ConsoleApplication2.Bar`1+<>c__DisplayClass0[ConsoleApplication2.Model]).id))
        Console.ReadKey();


        var subworkitems = barModel.list.Where(barModel.subFor(id).Compile());
                // Exception {"variable 'm' of type 'ConsoleApplication2.Model' referenced from scope '', but it is not defined"}

        Console.WriteLine(subworkitems.ToString());
        Console.ReadKey();
    }
}

class Bar<TModel>
{

    public Bar(Expression<Func<TModel, string>> foreignKeyExpression)
    {
        _foreignKeyExpression = foreignKeyExpression;
    }

    private Expression<Func<TModel, string>> _foreignKeyExpression { get; set; }

    public Expression<Func<TModel, bool>> subFor(string id)
    {

        var ex = forTargetId(id);

        return ex;
    }

    public Expression<Func<TModel, bool>> forTargetId(String id)
    {

        var fc = _foreignKeyExpression;

        Expression<Func<TModel, bool>> predicate = m => true;

        var result = predicate.And(m => fc.Invoke(m) ==开发者_StackOverflow id).Expand();

        return result;

    }

}

class Model
{
    public string key;
    public string value;
}

class BarModel : Bar<Model>
{
    public List<Model> list;

    public BarModel() : base(m => m.key) 
    {
        list = new List<Model>();
    }
}


}


Solution 1

This solution doesn't strip out the invoke statements which I assume you are trying to do by calling "Expand".

change the result of "forTargetId(String id)" to

predicate.And(m => fc.Invoke(m) == id);

When the expression is compiled in the where clause it will know that it needs to pass "m" to the fc expression above.

My first tip came when I changed

predicate.And(m => fc.Invoke(m) == id).Expand();

to

predicate.And(n => fc.Invoke(n) == id).Expand();

and I could see that n was not being pass along at all.

I tested out this change by manipulating the Main method as follows

static void Main(string[] args)
{
    var barModel = new BarModel();
    barModel.list.Add(new Model() { key = "1", value = "One" });
    barModel.list.Add(new Model() { key = "2", value = "Two" });
    barModel.list.Add(new Model() { key = "some", value = "Three" });

    string id = "some";

    Console.WriteLine(barModel.subFor(id).ToString());
    // output: m => (True AndAlso (m.key == value(ConsoleApplication2.Bar`1+<>c__DisplayClass0[ConsoleApplication2.Model]).id))
    Console.ReadKey();


    var subworkitems = barModel.list.Where(barModel.subFor(id).Compile());
    // Exception {"variable 'm' of type 'ConsoleApplication2.Model' referenced from scope '', but it is not defined"}

    foreach (var si in subworkitems)
    {
        Console.WriteLine(si.key);
        Console.WriteLine(si.value);
    }

    Console.WriteLine(subworkitems.ToString());
    Console.ReadKey();
}

Solution 2

This solution does remove the Invoke statements with the use of the Expand method but changes the way that you are anding the statements together.

The "Expand" function is advertised as following on the LinqKit website.

Expression<Func<Purchase, bool>> criteria1 = p => p.Price > 1000;
Expression<Func<Purchase, bool>> criteria2 = p => criteria1.Invoke(p) || p.Description.Contains("a");
Console.WriteLine(criteria2.Expand().ToString());
// p => ((p.Price > 1000) || p.Description.Contains("a"))

Notice that they aren't using the "And" method to put these things together but instead they are "daisy chaining" the calls together.

Instead of

Expression<Func<TModel, bool>> predicate = m => true;
var result = predicate.And(m => fc.Invoke(m) == id).Expand();

Do this

Expression<Func<TModel, bool>> predicate = m => fc.Invoke(m) == id && true;
return predicate.Expand();
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜