简体   繁体   English

C# 如何转换表达式<func<sometype> > 到表达式<func<othertype> > </func<othertype></func<sometype>

[英]C# How to convert an Expression<Func<SomeType>> to an Expression<Func<OtherType>>

I have used C# expressions before based on lamdas, but I have no experience composing them by hand.我以前使用过基于 lamda 的 C# 表达式,但我没有手工编写它们的经验。 Given an Expression<Func<SomeType, bool>> originalPredicate , I want to create an Expression<Func<OtherType, bool>> translatedPredicate .给定一个Expression<Func<SomeType, bool>> originalPredicate ,我想创建一个Expression<Func<OtherType, bool>> translatedPredicate

In this case SomeType and OtherType have the same fields, but they are not related (no inheritance and not based on a common interface).在这种情况下, SomeType 和 OtherType 具有相同的字段,但它们不相关(没有 inheritance 并且不基于公共接口)。

Background: I have a repository implementation based on LINQ to SQL. I project the LINQ to SQL entities to my Model entities, to keep my model in POCO.背景:我有一个基于 LINQ 到 SQL 的存储库实现。我将 LINQ 到 SQL 实体投影到我的 Model 实体,以将我的 model 保留在 POCO 中。 I want to pass expressions to the repository (as a form of specifications) but they should be based on the model entities.我想将表达式传递给存储库(作为规范的一种形式),但它们应该基于 model 实体。 But I can't pass those expressions to the data context, since it expects expressions based on the LINQ to SQL entities.但我无法将这些表达式传递给数据上下文,因为它需要基于 LINQ 到 SQL 实体的表达式。

With Expression , the simplest way is with a conversion expression :使用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 });
    }
}

Note however that this will be supported differently by different providers.但是请注意,不同的提供者将对此提供不同的支持。 EF might not like it, for example, even if LINQ-to-SQL does.例如,EF 可能不喜欢它,即使 LINQ-to-SQL 喜欢。

The other option is to rebuild the expression tree completely , using reflection to find the corresponding members.另一种选择是完全重建表达式树,使用反射找到相应的成员。 Much more complex.复杂得多。

There is one other way I have found, that also includes wrapping your original delegate.我发现了另一种方法,它还包括包装您的原始委托。

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

There is no implicit way to do the translation.没有隐含的方式来进行翻译。 You have to wrap your existing delegate inside a lambda that creates a new type from the argument type:您必须将现有委托包装在 lambda 中,该委托从参数类型创建新类型:

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

Where OtherTypeFromSomeType creates the OtherType instance from the SomeType argument.其中OtherTypeFromSomeTypeSomeType参数创建OtherType实例。

In my case, I had the same inconvenience, the problem was that I was using AutoMapper to pass information from one class to another and in this case @Marc-Gravell's solution did not work for me with AutoMapper, but doing some research I found that AutoMapper can convert Expression<Func<OrderLineDTO, bool>> to Expression<Func<OrderLine, bool>>就我而言,我遇到了同样的不便,问题是我使用 AutoMapper 将信息从一个 class 传递到另一个,在这种情况下,@Marc-Gravell 的解决方案不适用于我的 AutoMapper,但做了一些研究我发现AutoMapper 可以将Expression<Func<OrderLineDTO, bool>>转换为Expression<Func<OrderLine, bool>>

public class OrderLine
{
  public int Id { get; set; }
  public int OrderId { get; set; }
  public decimal Quantity { get; set; }
}

public class OrderLineDTO
{
  public int Id { get; set; }
  public int OrderId { get; set; }
  public decimal Quantity { get; set; }
}

The AutoMapper configuration would be the following AutoMapper 配置如下

var config = new MapperConfiguration(cfg =>
{
    cfg.AddExpressionMapping();

    cfg.CreateMap<OrderLine, OrderLineDTO>();
    cfg.CreateMap<OrderLineDTO, OrderLine>();
});

var mapper = config.CreateMapper();

And finally, perform the conversion最后,执行转换


Expression<Func<OrderLineDTO, bool>> expression = e => e.OrderId == 102;

Expression<Func<OrderLine, bool>> convertedExpression = mapper.Map<Expression<Func<OrderLine, bool>>>(expression);

This code depends on AutoMapper and AutoMapper.Extensions.ExpressionMapping此代码依赖于AutoMapperAutoMapper.Extensions.ExpressionMapping

For more information, you can see the documentation of AutoMapper and to perform the expressions有关详细信息,您可以查看AutoMapper的文档并执行表达式

I had the same issue as you and i fixed it like this with EF:我和你有同样的问题,我用 EF 解决了这个问题:

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

Entity Framework knows how to build the correct sql command.实体框架知道如何构建正确的 sql 命令。 Converting the expression is much more complicated, because it is built to be immutable and could cause undesired run time effects if you do something wrong, and, in my case at least, it is not needed.转换表达式要复杂得多,因为它是不可变的,如果你做错了什么,可能会导致不希望的运行时效果,至少在我的情况下,它是不需要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM