将表达式传递给 IEnumerable Select

Passing Expression to IEnumerable Select

我正在尝试编写从数据库实体到应用程序读取模型的自定义映射。

假设我有 2 个实体

public class A
{
    public string One {get; set;}
    public string Two {get; set;}
    public ICollection<B> Three {get;set;}

}

public class B
{
    public string SomeProp {get; set;}
    public string SomeProp2 {get; set;}
}

我正在尝试映射这些实体以读取模型。

public class AReadModel
{
    public string One {get; set;}
    public string Two {get; set;}
    public ICollection<BReadModel> Three {get;set;}
    public bool Deletable {get; set;}

}

public class BReadModel
{
    public string SomeProp {get; set;}
    public string SomeProp2 {get; set;}
}

此关系中的实体 B 是集合,但在另一个关系中它可以是独立的 1 : 1 或 1 : 0 关系,所以我想将 A 和 AReadModel 以及 B 和 BReadModel 之间的映射定义为将作为表达式传递的IQueryable Select()。关键是我想在 A -> AReadModel 的定义中重用 B -> BReadModel 的映射,并将其定义为 IQueryable 自定义扩展。

public static class BMapping
{
    public Expression<Func<B,BReadModel>> expression = instance => new BReadModel
    {
        SomeProp = instance.SomeProp,
        SomeProp2 = instance.SomeProp
    };

    public static IQueryable<BReadModel>ToReadModel(this IQueryable<B> source)
    {
        return this.Select(expression);
    }
}

public static class AMapping
{
    public Expression<Func<A,AReadModel>> expression = instance => new AReadModel
    {
        One = instance.One,
        Two = instance.Any,
        Deletable = Three.Any(),
        Three = instance.Three.Select(BMapping.expression) // this will not work cuz Three is collection and it requires Func<B,BReadModel> yet i need an expression here, otherwise EF won't be able to translate it. Of course I could just explicitly define mapping here again and it would work but it will lead to maintenance hell.

    public static IQueryable<AReadModel>ToReadModel(this IQueryable<A> source)
    {
        return this.Select(expression);
    }
}

所以问题是:是否可以在 AMapping 中嵌套集合的映射定义中重用 BMapping 中定义的映射?

您可以使用 Expression class 的 Compile() 方法 returns 对应的委托(参见 https://msdn.microsoft.com/en-us/library/bb345362(v=vs.110).aspx)。

通常它需要一些表达式 post 处理,如 LINQKit AsExpandable / Invoke / Expand,但幸运的是在这种特定情况下你可以简单地使用AsQueryable 在应用表达式之前,这是最新的 EF6 支持的。

因此,假设 exppressionBMapping class 的静态成员,以下应该有效:

Three = instance.Three.AsQueryable().Select(BMapping.expression)