使用 LinqKit 构建的 c# Lambda 表达式无法编译

发布于 2024-08-29 22:58:25 字数 1852 浏览 4 评论 0原文

这个 lambda 无法编译,但我不明白为什么。

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) == 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>();
    }
}


}

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) == 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>();
    }
}


}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尹雨沫 2024-09-05 22:58:25

解决方案 1

此解决方案不会删除调用语句,我假设您尝试通过调用“Expand”来执行此操作。

将“forTargetId(String id)”的结果更改为

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

当表达式在 where 子句中编译时,它会知道需要将“m”传递给上面的 fc 表达式。

,我的第一个提示出现了

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

当我更改为时

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

,我可以看到 n 根本没有被传递。

我通过如下操作 Main 方法测试了此更改。

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();
}

解决方案 2

此解决方案确实使用 Expand 方法删除了 Invoke 语句,但更改了将语句连接在一起的方式。

LinqKit 网站上对“Expand”功能的宣传如下。

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"))

请注意,他们没有使用“And”方法将这些内容放在一起,而是将调用“菊花链”在一起。

而不是

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

这样做

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

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文