Mapster嵌套导航属性映射时出现投影编译错误求助
Mapster投影编译错误排查与解决
全局配置
TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true);
数据库模型:AdapterMessage
public class AdapterMessage : BaseEntity<Guid>, IAggregateRoot { public string MessageID { get; set; } public string? MessageKey { get; set; } public string? ScenarioIdentifier { get; set; } public string? SenderName { get; set; } public string? ReceiverName { get; set; } public string? InterfaceName { get; set; } public string? Namespace { get; set; } public string? ErrorLabel { get; set; } public string? ErrorCode { get; set; } public DateTime? StartTime { get; set; } public DateTime? EndTime { get; set; } public DateTime? PersistUntil { get; set; } public DateTime? ValidUntil { get; set; } public string? MessageType { get; set; } public string? NodeId { get; set; } public string? QualityOfService { get; set; } public string? Status { get; set; } public string Size { get; set; } public bool? Cancelable { get; set; } public string? ConnectionName { get; set; } public string? Credential { get; set; } public string? Direction { get; set; } public bool? Editable { get; set; } public string? Endpoint { get; set; } public string? Headers { get; set; } public string? IsPersistent { get; set; } public string? Protocol { get; set; } public string? ReceiverInterfaceName { get; set; } public string? ReceiverInterfaceNameSpace { get; set; } public string? ReceiverPartyAgency { get; set; } public string? ReceiverPartyName { get; set; } public string? ReceiverPartySchema { get; set; } public bool? Restartable { get; set; } public string? Retries { get; set; } public string? RetryInterval { get; set; } public string? SenderInterfaceName { get; set; } public string? SenderInterfaceNameSpace { get; set; } public string? SenderPartyAgency { get; set; } public string? SenderPartyName { get; set; } public string? SenderPartySchema { get; set; } public string? SequenceNumber { get; set; } public string? SerializationContext { get; set; } public string? TimesFailed { get; set; } public string? Version { get; set; } public bool? WasEdited { get; set; } public string? PayloadPermissionWarning { get; set; } public string? Duration { get; set; } public string? MessagePriority { get; set; } public string? MessageListBytesXmlAM { get; set; } public string? MessageListBytesXmlBI { get; set; } public string? MessageListBytesXmlMS { get; set; } public string? MessageListBytesXmlSC { get; set; } public bool? IsMessageListBytesWritter { get; set; } public DateTime? LastAuditLogQueryDate { get; set; } public List<AdapterMessageAuditLog> AdapterMessageAuditLogs { get; set; } }
DTO类:AdapterMessageDto
using Application.Sap.AdapterMessageAuditLog; using Domain.Common.Contracts; namespace Application.Sap.AdapterMessage { public class AdapterMessageDto : BaseDto { public string MessageID { get; set; } public string? MessageKey { get; set; } public string? ScenarioIdentifier { get; set; } public string? SenderName { get; set; } public string? ReceiverName { get; set; } public string? InterfaceName { get; set; } public string? Namespace { get; set; } public int? ErrorLabel { get; set; } public string? ErrorCode { get; set; } public DateTime? StartTime { get; set; } public DateTime? EndTime { get; set; } public DateTime? PersistUntil { get; set; } public DateTime? ValidUntil { get; set; } public string? MessageType { get; set; } public int? NodeId { get; set; } public string? QualityOfService { get; set; } public string? Status { get; set; } public long Size { get; set; } public bool? Cancelable { get; set; } public string? ConnectionName { get; set; } public string? Credential { get; set; } public string? Direction { get; set; } public bool? Editable { get; set; } public string? Endpoint { get; set; } public string? Headers { get; set; } public bool? IsPersistent { get; set; } public string? Protocol { get; set; } public string? ReceiverInterfaceName { get; set; } public string? ReceiverInterfaceNameSpace { get; set; } public string? ReceiverPartyAgency { get; set; } public string? ReceiverPartyName { get; set; } public string? ReceiverPartySchema { get; set; } public bool? Restartable { get; set; } public int? Retries { get; set; } public int? RetryInterval { get; set; } public string? SenderInterfaceName { get; set; } public string? SenderInterfaceNameSpace { get; set; } public string? SenderPartyAgency { get; set; } public string? SenderPartyName { get; set; } public string? SenderPartySchema { get; set; } public int? SequenceNumber { get; set; } public string? SerializationContext { get; set; } public int? TimesFailed { get; set; } public int? Version { get; set; } public bool? WasEdited { get; set; } public string? PayloadPermissionWarning { get; set; } public long Duration { get; set; } public int? MessagePriority { get; set; } public string? MessageListBytesXmlAM { get; set; } public string? MessageListBytesXmlBI { get; set; } public string? MessageListBytesXmlMS { get; set; } public string? MessageListBytesXmlSC { get; set; } public bool? IsMessageListBytesWritter { get; set; } public DateTime? LastAuditLogQueryDate { get; set; } public List<AdapterMessageAuditLogDto> AdapterMessageAuditLogs { get; set; } } }
错误现象
注释掉DTO中的public List<AdapterMessageAuditLogDto> AdapterMessageAuditLogs { get; set; }属性时,映射正常;保留该属性时触发以下编译错误:
Error while compiling source=Domain.Entity.SAP.AdapterMessage destination=Application.Sap.AdapterMessage.AdapterMessageDto type=Projection
仓库实现代码
using Application.Common.Persistence; using Ardalis.Specification; using Ardalis.Specification.EntityFrameworkCore; using Domain.Common.Contracts; using Domain.Entity.Configuration; using Infrastructure.Persistance.Context; using Mapster; using Microsoft.EntityFrameworkCore.Storage; namespace Infrastructure.Persistance.Repository { // Inherited from Ardalis.Specification's RepositoryBase<T> public class ApplicationDbRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot { private readonly ApplicationDbContext _context; public ApplicationDbRepository(ApplicationDbContext dbContext) : base(dbContext) { _context = dbContext; } // We override the default behavior when mapping to a dto. // We're using Mapster's ProjectToType here to immediately map the result from the database. // This is only done when no Selector is defined, so regular specifications with a selector also still work. protected override IQueryable<TResult> ApplySpecification<TResult>(ISpecification<T, TResult> specification) => specification.Selector is not null ? base.ApplySpecification(specification) : ApplySpecification(specification, false) .ProjectToType<TResult>(); public IDbContextTransaction BeginTransaction() => _context.Database.BeginTransaction(); } }
解决方案
- 配置嵌套DTO映射
Mapster需要明确知道如何映射AdapterMessageAuditLog到AdapterMessageAuditLogDto,添加显式配置:
// 在全局配置中添加 TypeAdapterConfig<AdapterMessageAuditLog, AdapterMessageAuditLogDto>.NewConfig();
如果嵌套DTO有自定义类型转换,需补充对应的映射规则。
- 针对投影关闭引用保留
全局启用的PreserveReference(true)会干扰EF Core投影查询的逻辑,针对当前映射单独禁用:
TypeAdapterConfig.GlobalSettings.ForType<AdapterMessage, AdapterMessageDto>() .PreserveReference(false);
- 在Specification中包含关联集合
EF Core投影时需要显式加载关联数据,在对应的Specification中添加Include:
public class AdapterMessageSpecification : Specification<AdapterMessage> { public AdapterMessageSpecification() { Query.Include(x => x.AdapterMessageAuditLogs); } }
- 检查嵌套类型兼容性
确认AdapterMessageAuditLog与AdapterMessageAuditLogDto的所有属性类型可自动转换,若存在字符串转数字等非默认转换,需添加Mapster转换器。
内容的提问来源于stack exchange,提问作者Mustafa Ulukaya
相关产品推荐
相关产品推荐

