【问题标题】:C# How to convert an Expression<Func<SomeType>> to an Expression<Func<OtherType>>C# 如何将 Expression<Func<SomeType>> 转换为 Expression<Func<OtherType>>
【发布时间】:2010-10-13 20:09:48
【问题描述】:

我之前使用过基于 lamdas 的 C# 表达式,但我没有手工编写它们的经验。给定一个Expression&lt;Func&lt;SomeType, bool&gt;&gt; originalPredicate,我想创建一个Expression&lt;Func&lt;OtherType, bool&gt;&gt; translatedPredicate

在这种情况下 SomeType 和 OtherType 具有相同的字段,但它们不相关(没有继承并且不基于公共接口)。

背景:我有一个基于 LINQ to SQL 的存储库实现。我将 LINQ to SQL 实体投影到我的模型实体中,以将我的模型保存在 POCO 中。我想将表达式传递给存储库(作为规范的一种形式),但它们应该基于模型实体。但我无法将这些表达式传递给数据上下文,因为它需要基于 LINQ to SQL 实体的表达式。

【问题讨论】:

标签: c# linq-to-sql lambda repository-pattern expression-trees


【解决方案1】:

使用Expression,最简单的方法是使用转换表达式

class Foo {
    public int Value { get; set; }
}
class Bar {
    public int Value { get; set; }
}
static class Program {
    static void Main() {
        Expression<Func<Foo, bool>> predicate =
            x => x.Value % 2 == 0;
        Expression<Func<Bar, Foo>> convert =
            bar => new Foo { Value = bar.Value };

        var param = Expression.Parameter(typeof(Bar), "bar");
        var body = Expression.Invoke(predicate,
              Expression.Invoke(convert, param));
        var lambda = Expression.Lambda<Func<Bar, bool>>(body, param);

        // test with LINQ-to-Objects for simplicity
        var func = lambda.Compile();
        bool withOdd = func(new Bar { Value = 7 }),
             withEven = func(new Bar { Value = 12 });
    }
}

但是请注意,不同的供应商会对此提供不同的支持。例如,EF 可能不喜欢它,即使 LINQ-to-SQL 喜欢。

另一种选择是完全重建表达式树,使用反射找到相应的成员。复杂得多。

【讨论】:

  • 我希望 L2EF 喜欢这个,因为我将表达式从接口转换为具体类,并将结果作为 IQueryable 返回...你认为有办法吗减少开销?
  • +1:这让我走上了我认为的正确道路。即便如此,我确实学到了一些新东西。我从 EF 那里得到了一个缺失的关系。在我提出一个新问题之前,我会做更多的工作。我想知道是否需要通过 EntityCollection 方法附加表格...
【解决方案2】:

我发现了另一种方法,也包括包装您的原始委托。

Func<T, object> ExpressionConversion<U>(Expression<Func<T, U>> expression)
{
    Expression<Func<T, object>> g = obj => expression.Compile().Invoke(obj);
    return g.Compile();
}

【讨论】:

    【解决方案3】:

    没有隐含的方式来进行翻译。您必须将现有委托包装在 lambda 中,该 lambda 从参数类型创建新类型:

    var translatedPredicate = x => originalPredicate(OtherTypeFromSomeType(x))
    

    OtherTypeFromSomeTypeSomeType 参数创建OtherType 实例。

    【讨论】:

    • 问题是关于表达式,而不是代表。您不能使用这种方法来调用子表达式;它更复杂。
    • 糟糕,没有仔细阅读。无论如何,技术基本相同,表达式只是需要更多的工作(即使从技术上讲,我的代码在第一次编译 originalPredicate 并使用 Expression&lt;…&gt; 而不是 var 后仍然可以工作;-))。
    【解决方案4】:

    我和你有同样的问题,我用 EF 解决了这个问题:

    var viewModeValue = dbContext.Model.Select(m => new ViewModel{Field = m.Field}).Where(predicate) //predicate is an Expression<Func<ViewModel, bool>>
    

    Entity Framework 知道如何构建正确的 sql 命令。转换表达式要复杂得多,因为它是不可变的,如果你做错了可能会导致不希望的运行时效果,至少在我的情况下,它是不需要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多