c# - AutoMapper ProjectTo 继承问题

标签 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 似乎没有考虑列表中项目的类型,而是将它们强制转换为列表本身的类型。如果列表的类型是 FailureAlertEntity,那么显然它会起作用,但列表可以包含从 AlertEntity 派生的其他类型。

如果我想使用 Mapper.Map 对一个对象做同样的事情,它工作得很好:

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

结果:

yep

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

最佳答案

你不能那样做,但这是有充分理由的。 ProjectToIQueryable 的扩展,而不是 IEnumerable。什么是 IQueryable

Provides functionality to evaluate queries against a specific data source

The IQueryable interface is intended for implementation by query providers.

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

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

Select 中的内容是表达式(不是函数),它将由查询提供程序转换为数据源查询(如 SELECT Property from ... 到 sql服务器)。此时,查询未执行,甚至无法知道它将返回哪些子类型(如果有)的元素。您只能选择列的一个子集,您不能说“如果 Alert 是 FailureAlert,请给我这个列,如果是 AnotherAlert,请给我另一个列”,这样的表达式无法转换为数据存储查询。现在,例如, Entity Framework 可以处理它的实体的继承(例如每个类型的表继承),但此时它比自动映射器有更多的信息(例如,哪些表用于处理继承)。 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/

相关文章:

c# - NHibernate "Could not determine type for X"错误

c# - AngularJS 和 Bootstrap 在 IIS 7.5 中部署后无法正确呈现

c# - EF 迁移生成一个迁移文件,其中包含不再存在的数据

.net - 在 Windows 中添加全局键盘快捷键的最简单方法是什么?

c# - 如何确定一个矩形是否完全包含在另一个矩形中?

c# - 如何删除 XMLDocument 中的特定属性?

c# - 如何为绑定(bind)路径传递嵌入式控件的属性?

c# - 如何通过 AutoMapper 将匿名对象映射到类?

automapper,映射到接口(interface)

c# - Automapper 异常 : "Missing type map configuration or unsupported mapping."