EF Core 将实体映射到模型

问题描述 投票:0回答:1

我正在尝试有效地将实体映射到模型上。

我的实体是:

public class ParentEntity
{
    public int Id { get; set; }
    public string  Name { get; set; }
    public ChildEntity Child { get; set; }
}

public class ChildEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

我的模型是:

public class ParentModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ChildModel Child { get; set; }
}

public class ChildModel
{
    public int Id { get; set; }
    public string Name { get; set; }
}

(实际上,这些类之间会存在差异,但为了简化,这里不存在差异。)

我编写了一个扩展方法来进行映射:

public static IQueryable<ParentModel> ToParentModel (this IQueryable<ParentEntity> parentEntities)
{
    return parentEntities.Select(p => new ParentModel
    {
        Id = p.Id,
        Name = p.Name,
        Child = new ChildModel { Id = p.Child.Id, Name = p.Child.Name.ToLower()}
    });
}

ToLower()
是为了突出显示问题。

我可以运行它:

var parents = _context.Set<ParentEntity>().ToParentModel().ToArray();

生成的SQL为:

 SELECT "p"."Id", "p"."Name", "c"."Id", lower("c"."Name") AS "Name"
 FROM "Parents" AS "p"
 LEFT JOIN "Children" AS "c" ON "p"."ChildId" = "c"."Id" 

即小写处理在数据库中完成。

到目前为止一切都很好,除了关注点分离不太好。初始化

ChildModel
的代码与初始化
ParentModel
的代码位于同一位置。

我尝试在

ChildModel
中使用构造函数:

public ChildModel(ChildEntity ent)
{
    Id = ent.Id;
    Name = ent.Name.ToLower();
}

并在扩展方法中:

return parentEntities.Select(p => new ParentModel
{
    Id = p.Id,
    Name = p.Name,
    Child = new ChildModel (p.Child)
});

这可行,但生成的 SQL 不包含

lower
。转换为小写是在程序中完成的。

有什么办法可以边吃边吃蛋糕吗?

我仍然可以将 C# 代码转换为 SQL,但仍然以模块化方式构建我的 C# 代码吗?

c# entity-framework-core extension-methods sql-generation
1个回答
0
投票

EF Core 的

IQueryable
实现利用 ExpressionTrees 将尽可能多的工作推迟到数据库。

背景

来自微软:

表达式树表示树状数据结构中的代码,其中每个节点都是一个表达式,例如方法调用或二元运算,例如 'x < y'

当您将

Select
与模型
setters
一起使用时:

return parentEntities.Select(p => new ParentModel
{
    Id = p.Id,
    Name = p.Name,
    Child = new ChildModel { Id = p.Child.Id, Name = p.Child.Name.ToLower()}
});

这转化为一个表达式树,看起来像这样:

Expression-> Func<Parent, ParentModdel>
-- BinaryExpression.Assign-> ParentModel.Id->setter, Parent.Id->getter
-- BinaryExpression.Assign-> ParentModel.Name->setter, Parent.Name->getter
-- BinaryExpression.Assign-> ParentModel.Child->setter, Expression->Func<Parent, ChildModel>

// Expression->Func<Parent, ChildModel> looks something like:
BinaryExpression.Assign-> ChildModel.Id->setter, Parent.Child.Id->getter
BinaryExpression.Assign-> ChildModel.Name->setter, Parent.Child.Name->getter

我使用伪代码来简化树状结构的说明,因为 ExpressionTree 语法非常冗长并且可能非常令人困惑,并且坦率地说超出了当前问题的范围。请参阅在此处分配表达式

当您使用构造函数语法时:

Child = new ChildModel (p.Child)

无法有效推断其内容。构造函数被有效地视为方法,这意味着您可以将它们的参数、返回类型和调用描述为表达式,但不能描述其内容。因此 EF 没有选择,只能创建一个简单的表达式树,从 SQL 中获取 Parent 对象,然后在内存中执行

ChildModel
构造函数,从而调用 string.ToLower() 操作。

注意:可以读取方法内容,但这涉及反编译 IL,这对于数据库查询来说过于密集且缓慢。这就是 VS 在检查引用库中的方法时所做的事情

回答

AutoMapper 在程序启动时运行其重型反射来生成对象映射,正如您可能猜到的那样,它代表

ExpressionTree
。默认情况下,它使用命名约定,即它将映射
Id
->
Id
ChildId
->
ChildId
Child.Id
。它还公开 API 来为更复杂的场景定义您自己的自定义映射。

© www.soinside.com 2019 - 2024. All rights reserved.