【问题标题】:binding an Expression<Func> selector to a linq query将 Expression<Func> 选择器绑定到 linq 查询
【发布时间】:2016-01-15 01:36:57
【问题描述】:

我正在使用将 linq 表达式转换为事件查询的库。 (流洞察力)。

我需要解决这个问题:

给定一些值,以及一个组合值的表达式。

var a = new[] { "a", "b", "c" };
var b = new[] { "x", "y", "z" };
Expression<Func<string, string, string>> combine = (x, y) => x + ":" + y;

我需要将 combine 函数绑定到以下查询,同时仍保持表达式语法。

var query =
    from f in a
    from g in b
    select combine(f, g);

query.Dump();

很遗憾,此 linq 语句的目标应用程序不支持原始格式的 Func&lt;string, string, string&gt;,因为查询是在运行时解释的。

我收到此错误:其他信息:Microsoft 复杂事件处理 LINQ 提供程序不支持以下表达式:'Invoke(value(System.Func`3...

如果我可以重现表达式,我想我会解决这个问题:

var query =
    from f in a
    from g in b
    select x + ":" + y;

我想将查询的选择部分作为Expression&lt;Func&lt;... 参数传递给函数。

我试过了,显然不太好用:

var query =
    a.SelectMany(a1 => b, (x, y) => new { x, y })
     .Select(_ => combine(_.x, _.y));

我已经简化了这个问题,但作为参考,真正的查询看起来像这样:

        var ms =
            requestStream
                .AlterEventLifetime(
                    _ => _.StartTime - TimeSpan.FromMilliseconds(10000),
                    _ => _.EndTime + TimeSpan.FromMilliseconds(10000) - _.StartTime)
                .SelectMany(requestData => responseStream
                    .AlterEventLifetime(
                        _ => _.StartTime - TimeSpan.FromMilliseconds(10000),
                        _ => _.EndTime + TimeSpan.FromMilliseconds(10000) - _.StartTime),
                    (requestData, responseData) => new {requestData, responseData})
                .Where(@t => @t.requestData.CorrelationTicketId == @t.responseData.CorrelationTicketId)
                .Where(@t => @t.requestData.CorrelationTicketId > 0
                             && @t.requestData.SuccessFailureCode != 3001)
                .Select(@t => expression(@t.requestData, @t.responseData));

【问题讨论】:

  • .SelectMany(a1 =&gt; b, combine) 工作吗?
  • 否,CS1929 'string[]' 不包含 'SelectMany' 的定义和最佳扩展方法重载 'Queryable.SelectMany(IQueryable, Expression>>, Expression>)' 需要类型为 'IQueryable' 的接收器
  • 除此之外,我真的需要弄清楚如何将它绑定到 select,因为我的现实世界场景也有很多 where 子句。
  • 第一个错误是使用数组作为mock源引起的;如果您在a 上致电AsQueryable,它就会消失。我想你可能会从拨打.Select(expression) 而不是.Select(t =&gt; expression(...)) 获得一些乐趣

标签: c# linq


【解决方案1】:

您必须先编译表达式,然后才能将其用作函数:

var a = new[] { "a", "b", "c" };
var b = new[] { "x", "y", "z" };

Expression<Func<string, string, string>> combine = (x, y) => x + ":" + y;
var fu = combine.Compile();
var query =
        from f in a
        from g in b
        select fu(f, g);

【讨论】:

  • 有什么下降的原因吗?
  • 你是对的,这就是全部。很有魅力...我认为这会导致与 Func 相同的错误,但它不会'
  • 啊,表达式的生命,如此强大,还有很多值得学习的地方,虽然我的 +1 是好的答案
【解决方案2】:

HimBromBeere 的答案将起作用,如果您只打算将它与内存中的 IEnumerable&lt;string&gt; 源一起用于 ab,那么它就是您要选择的。

否则,虽然它仍然可以与 IQueryable&lt;string&gt; 一起使用,但它会通过将相关字符串拉入内存来实现这一点,而不是将表达式向下推送到提供程序以便它可以成为 SQL 查询等。

处理IQueryable&lt;string&gt; as IQueryable&lt;string&gt; 你会想要:

var query = a.AsQueryable().SelectMany(_ => b, combine);

(当在 IEnumerable&lt;string&gt; 上运行时,这会成为一种稍微昂贵的方式来执行 a.SelectMany(_ =&gt; b, combine.Compile());,这是编写 HimBromBeere 给出的答案的另一种方式)。

【讨论】:

    【解决方案3】:

    补充说明:

    Expression&lt;Func&lt;T&gt;&gt; 是未编译的表达式树。如果您要将它发送给需要解析表达式树并转换它们(而不仅仅是运行代码)的提供程序,例如实体框架,则需要使用它。

    您的案例涉及确实需要这样做的提供程序,而不是在使用前编译表达式(当您尝试使用实际的 StreamInsight 源重用该代码块时,这将不起作用)您最好直接调用 @987654322 @ 在模拟数组上,如下所示:

    // using function form of LINQ because it's more flexible
    // also, using the join because that's what your query does - a cartesian product
    var query = a.AsQueryable().Join(b.AsQueryable(), x => 0, x => 0, combine);
    

    我还建议您对 IQStreamable 接口提供程序和扩展方法(来自 StreamInsight)的实际工作方式进行一些研究,因为我不认为裸数组是模拟事件流的最佳选择。

    【讨论】:

    • 你不能调用像combine(f, g)这样的表达式。
    • 编译肯定是需要的。 Func<... expression compile>
    • @JonHanna 我的错。我使用 LINQ 的查询形式,所以我很少忘记你不能给它表达式。编辑+解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-01
    相关资源
    最近更新 更多