C# 如何转换表达式> 到表达式>

发布于 2024-07-14 18:13:30 字数 467 浏览 6 评论 0 原文

我之前使用过基于 lamda 的 C# 表达式,但我没有手动编写它们的经验。 给定一个Expression> originalPredicate,我想创建一个Expression> 翻译谓词

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

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

I have used C# expressions before based on lamdas, but I have no experience composing them by hand. Given an Expression<Func<SomeType, bool>> originalPredicate, I want to create an 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).

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. I want to pass expressions to the repository (as a form of specifications) but they should be based on the model entities. But I can't pass those expressions to the data context, since it expects expressions based on the LINQ to SQL entities.

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

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

发布评论

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

评论(5

绻影浮沉 2024-07-21 18:13:31

我遇到了和你一样的问题,我用 EF 修复了它:

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

实体框架知道如何构建正确的 sql 命令。 转换表达式要复杂得多,因为它被构建为不可变的,如果您做错了什么,可能会导致不良的运行时效果,并且至少在我的情况下,不需要它。

I had the same issue as you and i fixed it like this with 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. 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.

年少掌心 2024-07-21 18:13:30

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

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 喜欢它。

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

With Expression, the simplest way is with a conversion 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.

The other option is to rebuild the expression tree completely, using reflection to find the corresponding members. Much more complex.

不及他 2024-07-21 18:13:30

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

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 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();
}
方圜几里 2024-07-21 18:13:30

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

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

其中 OtherTypeFromSomeTypeSomeType 创建 OtherType 实例争论。

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:

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

Where OtherTypeFromSomeType creates the OtherType instance from the SomeType argument.

鹤仙姿 2024-07-21 18:13:30

就我而言,我也遇到了同样的不便,问题是我使用 AutoMapper 将信息从一个类传递到另一个类,在这种情况下,@Marc-Gravell 的解决方案不适用于 AutoMapper,但做了一些研究,我发现AutoMapper 可以将 Expression> 转换为 Expression>

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

AutoMapper 配置如下 最后

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

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

var mapper = config.CreateMapper();

,执行转换


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

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

此代码依赖于 AutoMapperAutoMapper.Extensions.ExpressionMapping

更多信息,可以查看 AutoMapper 并执行 表达式

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

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

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

For more information, you can see the documentation of AutoMapper and to perform the expressions

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