AutoMapper ProjectTo继承问题

Sha*_*han 3 .net c# automapper

如何使ProjectTo正确映射不同的派生类型,而不将其强制转换为基本类型?

这对于Mapper.Map可以正常工作,但对于ProjectTo则不能。

源类:(EntityFramework模型)

public class FailureAlertEntity : AlertEntity
{
    public FailureAlertEntity(int id, string description) : base(id)
    {
        Description = description;
    }

    public string Description { get; set; }
}

public class AlertEntity
{
    public AlertEntity(int id)
    {
        ID = id;
    }

    public int ID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

目标类别(DTO模型):

public class FailureAlert : Alert
{
    public FailureAlert(int id, string description) : base(id)
    {
        Description = description;
    }

    public string Description { get; set; }
}

public class Alert
{
    public Alert(int id)
    {
        ID = id;
    }

    public int ID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

有多个类从AlertEntity和Alert派生,我想在这两种类型之间进行映射,而不能将派生类型转换为基本类型。

组态:

Mapper.Initialize(cfg => {
    cfg.CreateMap<AlertEntity, Alert>()
          .Include<FailureAlertEntity, FailureAlert>();
    cfg.CreateMap<FailureAlertEntity, FailureAlert>();
});
Run Code Online (Sandbox Code Playgroud)

测试代码:

var entities = new List<AlertEntity>()
{
    new FailureAlertEntity(1, "foo"),
    new FailureAlertEntity(2, "bar")
};

var alerts = entities.AsQueryable().ProjectTo<Alert>();
Run Code Online (Sandbox Code Playgroud)

结果:

不

ProjectTo似乎没有考虑列表中项目的类型,而是将其强制转换为List本身的类型。如果列表的类型为FailureAlertEntity,则显然可以使用,但是列表可以包含其他来自AlertEntity的类型。

如果我想对使用Mapper.Map的对象执行相同的操作,则可以正常工作:

var faEntity = new FailureAlertEntity(1, "asd");
var dto = Mapper.Map<Alert>(faEntity);
Run Code Online (Sandbox Code Playgroud)

结果:

是的

如何使ProjectTo映射类型像Mapper.Map一样?我假设他们都将使用相同的配置。

Evk*_*Evk 6

您不能这样做,但是有充分的理由。ProjectTo是的扩展IQueryable,不是IEnumerable。什么IQueryable

提供评估针对特定数据源的查询的功能

IQueryable接口旨在由查询提供程序实现。

因此IQueryable表示通过查询提供程序(例如Entity Framework)对数据源(例如sql服务器数据库)的查询。ProjectTo将构建一个Select 表达式,但实际上不会在内存中投影任何内容。因此,它将这样做很麻烦:

entities.AsQueryable().Select(c => new Alert() {Property = c.Property})
Run Code Online (Sandbox Code Playgroud)

里面的东西Select是表达式(不是函数),查询提供程序会将其转换为数据源查询(如SELECT Property from ...sql server)。目前,查询尚未执行,甚至无法知道它将返回哪个子类型(如果有)的元素。您只能选择列的一个子集,不能说“如果Alert是FailureAlert,请给我此列,如果是AnotherAlert,请给我另一列”,这样的表达式不能转换为数据存储查询。现在,例如,Entity Framework可以处理其实体的继承(如每个类型的表的继承),但是与自动映射器相比,它具有更多信息(例如,哪些表用于处理继承)。Automapper只有要映射的类型和要映射的类型。

因此,鉴于以上所述-AutoMapper不可能以另一种方式进行操作。

现在,在您的示例中,您正在使用“ fake” IQueryable。如果在实际应用中是这种情况,请使用IEnumerable和:

var alerts = Mapper.Map<List<Alert>>(entities);
Run Code Online (Sandbox Code Playgroud)

要么

var alerts = entities.Select(Mapper.Map<Alert>).ToList();
Run Code Online (Sandbox Code Playgroud)

如果您有real IQueryable,例如Entity Framework查询,它可以真正返回AlertEntity带有子类型的列表-您必须先实现查询,然后再使用automapper映射它(当然,这将实现具有所有属性的实体):

var alerts = yourQuery.AsEnumerable().Select(Mapper.Map<Alert>).ToList();
Run Code Online (Sandbox Code Playgroud)