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();
精彩评论