使用ASP.NET Core 2.2和Automapper 9.0.0时出现以下问题:
我有一个映射到dto的Entity,效果很好。在该实体内部还有一些实体。这些不能被扁平化,因为我们的客户也需要它们。但是,在这些嵌套实体中有一个属性需要附加映射。
在重叠实体的Profile类中,我这样做:
public class OperationalToPalletResultProfile : Profile
{
public OperationalToPalletResultProfile()
{
string lang = null;
CreateMap<TblDatOperationalHUTrace,PalletResult>()
.ForMember(dest => dest.TransCode,act => act.MapFrom((src,_,transactionCode,ctx) =>
ctx.Mapper.Map<TblLstCode,TransactionCodeResult>(src.TransCode)));
}
}
这有效地将TblLstCode正确映射到TransactionCodeResult。但是,TransactionCodeResult的“ DisplayDesc”属性仍然为空...
注意:“ lang”属性是通过“ ProjectTo”方法设置的:
var values = await query
.Take(5000)
.ProjectTo<PalletResult>(mapper.ConfigurationProvider,new { lang = language })
.ToListAsync()
.ConfigureAwait(false);
我尝试过:
1-在Profile类中使用“ AfterMap”(结果:DisplayDesc = null)
CreateMap<TblDatOperationalHUTrace,TransactionCodeResult>(src.TransCode,opt =>
opt.AfterMap((source,destination) =>
destination.DisplayDesc = $"[{destination.ShortName}] {destination.DescToLanguageDesc(destination,lang)}"))))
注意:DescToLanguageDesc是一个可以通过反射在实体中找到属性的函数(此方法有效,且不属于问题的一部分)
2-创建要在子实体的配置文件中使用的IMappingaction(结果:DisplayDesc = null)
public class TransactionCodeTranslation : IMappingaction<TblLstCode,TransactionCodeResult>
{
private readonly IHttpContextaccessor httpContextaccessor;
public TransactionCodeTranslation(IHttpContextaccessor httpContextaccessor)
{
this.httpContextaccessor = httpContextaccessor;
}
public void Process(TblLstCode source,TransactionCodeResult destination,ResolutionContext context)
{
//Get language from httpContext - this works correctly
destination.DisplayDesc = $"[{source.ShortName}] {source.DescToLanguageDesc(source,language)}";
}
}
public class TransactionCodeProfile : Profile
{
public TransactionCodeProfile()
{
string language = string.Empty;
CreateMap<TblLstCode,TransactionCodeResult>()
.AfterMap<TransactionCodeTranslation>();
}
}
这不起作用。但是,如果我直接使用选项2:
var tc = await codeRepo.TableNoTracking.FirstOrDefaultAsync(x => x.ShortName == "[something]").ConfigureAwait(false);
var transactionCode = mapper.Map<TblLstCode,TransactionCodeResult>(tc);
然后它可以正常工作!但这意味着我将不得不循环执行结果并再次映射结果中的每个对象...
有没有办法像选项1一样?
谢谢!
编辑1:
我应Lucian Bargaoanu的要求添加了一个BuildExecutionPlan:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TblDatOperationalHUTrace,PalletResult>()
.ForMember(dest => dest.TransCode,ctx) =>
ctx.Mapper.Map<TblLstCode,opt =>
opt.AfterMap((source,destination) =>
destination.DisplayDesc = $"[{destination.ShortName}] {destination.DescToLanguageDesc(destination,language)}"))));
cfg.CreateMap<TblLstCode,TransactionCodeResult>()
.AfterMap<TransactionCodeTranslation>();
});
var expression = config.BuildExecutionPlan(typeof(TblDatOperationalHUTrace),typeof(PalletResult));
var expression2 = config.BuildExecutionPlan(typeof(TblLstCode),typeof(TransactionCodeResult));
表达结果:
(src,dest,ctxt) =>
{
PalletResult typeMapDestination;
return (src == null)
? null
: {
typeMapDestination = dest ?? new PalletResult();
try
{
var resolvedValue = mappingFunction.Invoke(
src,typeMapDestination,typeMapDestination.TransCode,ctxt);
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.TransCode = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",ex,AutoMapper.TypePair,TypeMap,PropertyMap);
return null;
}
return typeMapDestination;
};
}
结果表达式2:
(src,ctxt) =>
{
TransactionCodeResult typeMapDestination;
return (src == null)
? null
: {
typeMapDestination = dest ?? new TransactionCodeResult();
try
{
var resolvedValue = ((src == null) || false) ? default(int) : src.Id;
typeMapDestination.Id = resolvedValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",PropertyMap);
return default(int);
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.ShortName;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.ShortName = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? default(int) : src.CodeTypeId;
typeMapDestination.CodeTypeId = resolvedValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",PropertyMap);
return default(int);
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.descLC;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescLC = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.descEN;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescEN = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.descFR;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescFR = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.descGE;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescGE = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.descNL;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescNL = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",PropertyMap);
return null;
}
afterFunction.Invoke(src,ctxt);
return typeMapDestination;
};
}
编辑2:
好的,我实际上找到了解决方案。我无缘无故地把事情复杂化了...
我将OperationalToPalletResultProfile更改为:
public class OperationalToPalletResultProfile : Profile
{
public OperationalToPalletResultProfile()
{
CreateMap<TblDatOperationalHUTrace,PalletResult>();
}
}
TransactionCodeProfile保持不变:
public class TransactionCodeProfile : Profile
{
public TransactionCodeProfile()
{
CreateMap<TblLstCode,TransactionCodeResult>()
.AfterMap<TransactionCodeTranslation>();
}
}
然后在我使用映射的地方,我对此进行了更改:
//results as TblDatOperationalHUTrace
var queryresult = await query
.Take(5000)
//.ProjectTo<PalletResult>(mapper.ConfigurationProvider) Don't do the mapping here anymore
.ToListAsync()
.ConfigureAwait(false);
//Map results to PalletResult
var values = mapper.Map<List<TblDatOperationalHUTrace>,List<PalletResult>>(queryresult); //Do the mapping here