AutoMapper ProjectTo继承问题

时间:2017-03-30 09:14:24

标签: c# .net 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; }
}

目标类(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; }
}

有多个来自AlertEntity和Alert的类,我想在这两种类型之间进行映射,而不会将派生类型转换为它们的基类型。

配置:

Mapper.Initialize(cfg => {
    cfg.CreateMap<AlertEntity, Alert>()
          .Include<FailureAlertEntity, FailureAlert>();
    cfg.CreateMap<FailureAlertEntity, FailureAlert>();
});

测试代码:

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

var alerts = entities.AsQueryable().ProjectTo<Alert>();

结果:

nope

ProjectTo似乎没有考虑列表中项目的类型,而是将它们转换为List本身的类型。如果List的类型是FailureAlertEntity,那么显然它可以工作,但列表可以包含从AlertEntity派生的其他类型。

如果我想对使用Mapper.Map的对象做同样的事情,它可以正常工作:

var faEntity = new FailureAlertEntity(1, "asd");
var dto = Mapper.Map<Alert>(faEntity);

结果:

yep

如何制作Mapper.Map等ProjectTo地图类型呢?我以为他们都会使用相同的配置。

1 个答案:

答案 0 :(得分:4)

你做不到,但有充分的理由。 ProjectToIQueryable的延伸,而不是IEnumerable。什么是IQueryable

  

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

     

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

因此IQueryable表示通过查询提供程序(如实体框架)对数据源(如sql server数据库)的查询。 ProjectTo将构建Select 表达式,它实际上不会在内存中投影任何内容。所以它会做到这一点:

entities.AsQueryable().Select(c => new Alert() {Property = c.Property})

Select内的东西是表达式(不是函数),它将由查询提供者转换为数据源查询(如SELECT Property from ...到sql server)。此时,查询不会被执行,甚至不可能知道它将返回哪些子类型(如果有的话)的元素。您只能选择列的一个子集,如果Alert是FailureAlert,则不能说#34;如果是AnotherAlert&#34;则给另一个列,这样的表达式无法转换为数据存储查询。现在,Entity Framework例如可以处理它的实体的继承(比如table-per-type inheritance),但是它具有更多的信息(例如,哪些表用于处理继承),而不是自动化器。 Automapper只有要映射的类型和要映射到的类型。

因此,鉴于上述情况,AutoMapper无法以另一种方式进行此操作。

现在,在您的示例中,您正在使用&#34; fake&#34; IQueryable。如果是实际应用中的情况 - 只需使用IEnumerable和:

var alerts = Mapper.Map<List<Alert>>(entities);

var alerts = entities.Select(Mapper.Map<Alert>).ToList();

如果您有真正的IQueryable,例如实体框架查询可以真正返回带有子类型的AlertEntity列表 - 您必须在使用automapper映射它之前实现您的查询(当然这将实现实体所有属性):

var alerts = yourQuery.AsEnumerable().Select(Mapper.Map<Alert>).ToList();