【问题标题】:Passing Expression to IEnumerable Select将表达式传递给 IEnumerable Select
【发布时间】:2017-01-10 12:27:36
【问题描述】:

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

假设我有 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 是 Collection 但在另一个中它可以是独立的 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 中定义的映射?

【问题讨论】:

  • 为什么不直接使用普通的 EF,然后使用 AutoMapper 转换为您的自定义类。但我想知道.. 你想达到什么目的.. 为什么首先使用 ReadModels?
  • @Seabizkit 答案很简单。带有延迟加载的 EF 让您只下载实体中包含的数据。让我们假设三个集合映射到一个表,并且这个集合(表)的大小在应用程序运行期间将至少以线性方式增加(比如 A 是一个测试,B 是一个测试实例)。所以一个测试可以有数千个测试实例。我不想沿着我的实体下载这么大的集合,但我需要一些只能在这个集合上计算的数据。 (示例:Deletable = Three.Any())(在应用端的 DB 很便宜)
  • 换句话说,ReadModels 让我将大量计算移至数据库并由 ReadModel 通过仅对 db 的一次查询来构建 + i 保护自己免受无意中将大型集合从 db 下载到应用程序的应用程序(通过我意味着通过简单的查询在实体上调用三个集合上的任何一个,这将导致下载三个集合只是为了检查它的空性)。
  • @user2184057 延迟加载与我所说的任何事情有关。你有一个包含实体集合的实体......好吧,现在你担心卷......好吧,但你并没有真正解释问题出在哪里。过滤你需要的东西,只使用它..简单。尝试用简单的术语解释你想要克服什么......看起来像处理大量......你是否在你的问题“大量计算”中抽象了一些细节这些是如何存储的。你能用上面代码的用例更新一下吗?

标签: c# .net entity-framework expression ienumerable


【解决方案1】:

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

【讨论】:

  • 问题是 EF 不适用于 Delegates -> 它适用于 Expression,如果我将使用 compile,我将丢失有关 EF 所需的表达式树的所有信息。 (它将解析表达式树,无法翻译方法调用 ToReadModel)。
  • 这个问题不是关于使代码编译,而是以允许实体框架稍后翻译它的方式传递这个表达式。
【解决方案2】:

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

因此,假设 exppressionBMapping 类的静态成员,以下应该可以工作:

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

【讨论】:

  • 谢谢,这正是我所需要的。
猜你喜欢
  • 2012-04-02
  • 1970-01-01
  • 2020-02-25
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多