簡體   English   中英

C# 如何轉換表達式<func<sometype> > 到表達式<func<othertype> > </func<othertype></func<sometype>

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

我以前使用過基於 lamda 的 C# 表達式,但我沒有手工編寫它們的經驗。 給定一個Expression<Func<SomeType, bool>> originalPredicate ,我想創建一個Expression<Func<OtherType, bool>> translatedPredicate

在這種情況下, SomeType 和 OtherType 具有相同的字段,但它們不相關(沒有 inheritance 並且不基於公共接口)。

背景:我有一個基於 LINQ 到 SQL 的存儲庫實現。我將 LINQ 到 SQL 實體投影到我的 Model 實體,以將我的 model 保留在 POCO 中。 我想將表達式傳遞給存儲庫(作為規范的一種形式),但它們應該基於 model 實體。 但我無法將這些表達式傳遞給數據上下文,因為它需要基於 LINQ 到 SQL 實體的表達式。

使用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 喜歡。

另一種選擇是完全重建表達式樹,使用反射找到相應的成員。 復雜得多。

我發現了另一種方法,它還包括包裝您的原始委托。

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

沒有隱含的方式來進行翻譯。 您必須將現有委托包裝在 lambda 中,該委托從參數類型創建新類型:

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

其中OtherTypeFromSomeTypeSomeType參數創建OtherType實例。

就我而言,我遇到了同樣的不便,問題是我使用 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; }
}

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的文檔並執行表達式

我和你有同樣的問題,我用 EF 解決了這個問題:

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

實體框架知道如何構建正確的 sql 命令。 轉換表達式要復雜得多,因為它是不可變的,如果你做錯了什么,可能會導致不希望的運行時效果,至少在我的情況下,它是不需要的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM