未调用 AutoMapper ConvertUsing

Ole*_* Sh 1 c# automapper

我有一个简单的任务:只需将一个类映射到另一个类。对于某些字段,我有复杂的逻辑,取决于 2 个或更多字段,因此,我尝试使用ConvertUsinghttps://docs.automapper.org/en/stable/Custom-type-converters.html

我使用 AutoMapper 10.0.0

我的代码是:

源码类:

public class DeviceStatusHistory
{
    public DeviceStatusHistory()
    {
        DateChange = DateTime.UtcNow;
    }

    public int Id { get; set; }
    public int DeviceId { get; set; }
    public virtual Device Device { get; set; }

    public int? RequestId { get; set; }
    public virtual DeviceManagementRequest Request { get; set; }

    public DeviceStatus OldStatus { get; set; }
    public DeviceStatus NewStatus { get; set; }
    public string Notes { get; set; }
    public DateTime DateChange { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

DTO类:

public class DeviceChangeStatusDto
{
    public int DeviceId { get; set; }
    public string CarrierName { get; set; }
    public string DeviceName { get; set; }
    public string DeviceIMEI { get; set; }
    public string OldStatus { get; set; }
    public string NewStatus { get; set; }
    public string Reason { get; set; }
    public DateTime DateChange { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

和自动映射器类:

public class AutoMapperEfDeviceManagement : AutoMapper.Profile
{
    public AutoMapperEfDeviceManagement()
    {
        CreateMap<DeviceStatusHistory, DeviceChangeStatusDto>().ConvertUsing<DeviceChangeStatusConverter>();
    }
}
Run Code Online (Sandbox Code Playgroud)

其中DeviceChangeStatusConverter定义为:

public class DeviceChangeStatusConverter : ITypeConverter<DeviceStatusHistory, DeviceChangeStatusDto>
{
    public DeviceChangeStatusDto Convert(DeviceStatusHistory source, DeviceChangeStatusDto destination, ResolutionContext context)
    {
        destination = new DeviceChangeStatusDto
        {
            CarrierName = source.Device.CarrierId.HasValue ? source.Device.Carrier.Name : null,
            DeviceId = source.DeviceId,
            DateChange = source.DateChange,
            DeviceIMEI = source.Device.IMEI,
            DeviceName = source.Device.GetFriendlyDetailedName(),
            NewStatus = CommonHelper.SplitByWords(source.NewStatus.ToString())
        };

        // some complex logic here

        return destination;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是当我尝试绘制它时:

var list = _context.DeviceStatusHistory
.Where(a => ((int)a.NewStatus < 100) && a.DateChange.Date == date.Date)
.ProjectTo<DeviceChangeStatusDto>(_mapperConfig)
.ToList();
Run Code Online (Sandbox Code Playgroud)

哪里_mapperConfig

        _mapperConfig = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile<AutoMapperEfDeviceManagement>();
        });
Run Code Online (Sandbox Code Playgroud)

它的映射看起来很简单:

CreateMap<DeviceStatusHistory, DeviceChangeStatusDto>();
Run Code Online (Sandbox Code Playgroud)

因此,仅映射相同的属性,不会调用转换器(调试器也这么说)。怎么了?

添加:

方法如下:

        CreateMap<DeviceStatusHistory, DeviceChangeStatusDto>()
            .ConvertUsing((source, destination) =>
            {
                destination.CarrierName = source.Device.CarrierId.HasValue ? source.Device.Carrier.Name : null;
Run Code Online (Sandbox Code Playgroud)

也不起作用

Gur*_*ron 5

长话短说

\n

这不是一个错误,而是一个已知的限制。基本上 AutoMapper 无法将某些自定义方法转换为 SQL(或将其转换为允许 ORM 将其转换为 SQL 的形式),因此它无法在ProjectTo.

\n

更多细节:

\n

文档中:

\n
\n

.ProjectTo<OrderLineDTO>()告诉AutoMapper\xe2\x80\x99s 映射引擎向 发出一个select子句,该IQueryable子句将通知实体框架它只需要查询Name表的列Item,就像您手动将您投影IQueryableOrderLineDTOwith aSelect子句一样。

\n
\n

自定义类型转换部分:

\n
\n

有时,您需要完全替换从源类型到目标类型的类型转换。在正常的运行时映射中,这是通过该ConvertUsing方法完成的。要在 LINQ 投影中执行模拟,请使用以下ConvertUsing方法:cfg.CreateProjection<Source, Dest>().ConvertUsing(src => new Dest { Value = 10 });
\n基于表达式的重载比基于重载的ConvertUsing限制稍多一些,因为只有在和底层 LINQ 提供程序中允许的内容才会起作用。FuncConvertUsingExpression

\n
\n

支持的映射选项

\n
\n

并非所有映射选项都受支持,因为生成的表达式必须由 LINQ 提供程序解释。AutoMapper 仅支持 LINQ 提供程序支持的内容:

\n
\n
\n
    \n
  • MapFrom(基于表达式)
  • \n
  • ConvertUsing(基于表达式)
  • \n
  • 忽略
  • \n
  • 空替代
  • \n
  • 价值转换器
  • \n
  • 包括成员
  • \n
\n
\n
\n

不支持:

\n
\n
\n
    \n
  • 健康)状况
  • \n
  • 设置映射顺序
  • \n
  • 使用目标值
  • \n
  • MapFrom(基于函数)
  • \n
  • 之前/之后地图
  • \n
  • 自定义解析器
  • \n
  • 自定义类型转换器
  • \n
  • 路径
  • \n
  • 价值转换器
  • \n
  • 域对象上的任何计算属性
  • \n
\n
\n

  • @OlegSh,这是否是“简单的事情”并不重要。如果它是基于“Func”或基于“Expression”,那么重要的是。您使用的重载接受 `Func&lt;...&gt;` (检查签名),因此它无法转换为 SQL。 (2认同)
  • @OlegSh是的,这样的表达式可以被翻译,但是这里使用的重载 - `.ConvertUsing((source, destination) =&gt; { destination.CarrierName = source.Device.CarrierId.HasValue ? source.Device.Carrier.Name : null;`不是一个表达式,它是一个 `Func` (检查签名)。并且 `Func` 是不可翻译的(至少没有一些肮脏的魔法)。 (2认同)