如何使 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>();

结果:

c# - AutoMapper ProjectTo 继承问题-LMLPHP

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

如果我想使用 Mapper.Map 对一个对象做同样的事情,它工作得很好:
var faEntity = new FailureAlertEntity(1, "asd");
var dto = Mapper.Map<Alert>(faEntity);

结果:

c# - AutoMapper ProjectTo 继承问题-LMLPHP

我怎样才能像 Mapper.Map 那样制作 ProjectTo map 类型?我假设他们都会使用相同的配置。

最佳答案

你不能这样做,但有一个很好的理由。 ProjectToIQueryable 的扩展,而不是 IEnumerable 。什么是 IQueryable



因此 IQueryable 表示通过查询提供程序(如 Entity Framework )对数据源(如 sql server 数据库)的查询。 ProjectTo 将构建一个 Select 表达式,它实际上不会在内存中转换任何内容。所以它会粗略地这样做:

entities.AsQueryable().Select(c => new Alert() {Property = c.Property})
Select 中的内容是表达式(不是函数),它将由查询提供程序转换为数据源查询(如 SELECT Property from ... 到 sql server)。此时,查询未执行,甚至无法知道它将返回哪些子类型(如果有)的元素。您只能选择列的一个子集,您不能说“如果 Alert 是 FailureAlert,则给我此列,但如果是 AnotherAlert,则给我另一列”,这样的表达式无法转换为数据存储查询。现在,Entity Framework 例如可以处理它的实体的继承(如 table-per-type 继承),但此时它比 automapper 具有更多的信息(例如,哪些表用于处理继承)。 Automapper 只有类型映射和类型映射到。

因此,鉴于上述情况 - AutoMapper 不可能以另一种方式做到这一点。

现在,在您的示例中,您使用的是“假” IQueryable 。如果在实际应用中是这种情况 - 只需使用 IEnumerable 和:
var alerts = Mapper.Map<List<Alert>>(entities);

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

如果您有真正的 IQueryable ,例如 Entity Framework 查询,它可以真正返回带有子类型的 AlertEntity 列表 - 您必须在使用 automapper 映射之前具体化您的查询(当然这将具体化具有所有属性的实体):
var alerts = yourQuery.AsEnumerable().Select(Mapper.Map<Alert>).ToList();

关于c# - AutoMapper ProjectTo 继承问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43113400/

10-08 22:21