简体   繁体   English

AutoMapper ProjectTo继承问题

[英]AutoMapper ProjectTo inheritance issue

How can I make ProjectTo map different derived types correctly without it casting them down to the base type? 如何使ProjectTo正确映射不同的派生类型,而不将其强制转换为基本类型?

This works fine with Mapper.Map, but not with ProjectTo. 这对于Mapper.Map可以正常工作,但对于ProjectTo则不能。

Source classes: (EntityFramework models) 源类:(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; }
}

Target classes (DTO models): 目标类别(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; }
}

There are multiple classes deriving from AlertEntity and Alert, and I would like to map between these two types without the derived types being casted down to their base types. 有多个类从AlertEntity和Alert派生,我想在这两种类型之间进行映射,而不能将派生类型转换为基本类型。

Configuration: 组态:

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

Test code: 测试代码:

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

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

Result: 结果:

不

ProjectTo doesn't seem to consider the types of the items in the list, but casts them to the type of List itself. ProjectTo似乎没有考虑列表中项目的类型,而是将其强制转换为List本身的类型。 If the type of the List is FailureAlertEntity, then obviously it would work, but the list could contain other types deriving from AlertEntity. 如果列表的类型为FailureAlertEntity,则显然可以使用,但是列表可以包含其他来自AlertEntity的类型。

If I wanted to do the same for an object using Mapper.Map, it works just fine: 如果我想对使用Mapper.Map的对象执行相同的操作,则可以正常工作:

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

Result: 结果:

是的

How can I make ProjectTo map types like Mapper.Map does? 如何使ProjectTo映射类型像Mapper.Map一样? I assumed they would both use the same configuration. 我假设他们都将使用相同的配置。

You cannot do that, but there is a good reason why. 您不能这样做,但是有充分的理由。 ProjectTo is extension of IQueryable , not IEnumerable . ProjectToIQueryable而不是IEnumerable扩展。 What is IQueryable ? 什么是IQueryable

Provides functionality to evaluate queries against a specific data source 提供评估针对特定数据源的查询的功能

The IQueryable interface is intended for implementation by query providers. IQueryable接口旨在由查询提供程序实现。

So IQueryable represents a query to data source (like sql server database) via query provider (like Entity Framework). 因此, IQueryable表示通过查询提供程序(例如Entity Framework)对数据源(例如sql服务器数据库)的查询。 ProjectTo will build a Select expression , it will not actually project anything in memory. ProjectTo将构建一个Select 表达式 ,它实际上不会在内存中投影任何内容。 So it will do rougly this: 因此,它将这样做很麻烦:

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

Where thing inside Select is expression (not a function) which will be translated by query provider to the data source query (like SELECT Property from ... to sql server). Select是表达式(不是函数),查询提供程序会将其转换为数据源查询(例如SELECT Property from ...到sql server)。 At this moment, query is not executed and it's not even possible to know elements of which subtypes (if any) it will return. 目前,查询尚未执行,甚至无法知道它将返回哪些子类型(如果有)的元素。 You can select only one subset of columns, you cannot say "give me this columns if Alert is FailureAlert, but another one if it's AnotherAlert", such expression cannot be translated to data storage query. 您只能选择一个列子集,不能说“如果Alert是FailureAlert,请给我此列,如果是AnotherAlert,请给我另一列”,这样的表达式不能转换为数据存储查询。 Now, Entity Framework for example can handle inheritance of it's entities (like table-per-type inheritance) but it has more info (which tables are used to handle inheritance for example) than automapper at this point. 现在,例如,Entity Framework可以处理其实体的继承(如每个类型的表的继承),但是与自动映射器相比,它具有更多信息(例如,哪些表用于处理继承)。 Automapper only has type to map from and type to map to. Automapper只有要映射的类型和要映射的类型。

So given the above - it's just not possible for AutoMapper to do it another way. 因此,鉴于以上所述-AutoMapper不可能以另一种方式进行操作。

Now, in your example you are using "fake" IQueryable . 现在,在您的示例中,您正在使用“ fake” IQueryable If that is the case in real application - just use IEnumerable and: 如果在实际应用中是这种情况,则只需使用IEnumerable并:

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

or 要么

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

If you have real IQueryable , for example Entity Framework query that can really return list of AlertEntity with subtypes - you have to materialize your query before mapping it with automapper (of course this will materialize entities with all properties): 如果您具有真正的IQueryable ,例如实体框架查询可以真正返回带有子类型的AlertEntity列表-您必须在通过自动映射器将其映射之前实现查询(当然,它将实现具有所有属性的实体):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM