Automapper: сопоставить объект внутри профиля, используя параметр - PullRequest
1 голос
/ 09 января 2020

У меня есть следующая проблема с использованием ASP. NET Core 2.2 и Automapper 9.0.0:

У меня есть объект, который я сопоставляю с dto, это прекрасно работает. Внутри этой сущности также есть несколько сущностей. Они не могут быть сглажены, поскольку они также необходимы нашему клиенту. Однако в этих вложенных сущностях есть одно свойство, которое требует дополнительного сопоставления.

в классе 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. Однако свойство «DisplayDes c» TransactionCodeResult остается пустым ...

Примечание: свойство "lang" устанавливается с помощью метода "ProjectTo":

var values = await query
        .Take(5000)
        .ProjectTo<PalletResult>(mapper.ConfigurationProvider, new { lang = language })
        .ToListAsync()
        .ConfigureAwait(false);

У меня есть попробовал:

1- Использование «AfterMap» в классе профиля (результат: DisplayDes c = null)

CreateMap<TblDatOperationalHUTrace, PalletResult>()
            .ForMember(dest => dest.TransCode, act => act.MapFrom((src, _, transactionCode, ctx) =>
                ctx.Mapper.Map<TblLstCode, TransactionCodeResult>(src.TransCode, opt =>
                    opt.AfterMap((source, destination) =>
                        destination.DisplayDesc = $"[{destination.ShortName}] {destination.DescToLanguageDesc(destination, lang)}"))))

Примечание: DescToLanguageDes c - это функция, которая находит свойство в объект с отражением (это работает и не является частью проблемы)

2- Создание действия IMappingAction для использования в профиле SubEntity (результат: DisplayDes c = 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);

Тогда он работает правильно! Но это означало бы, что мне понадобится l oop моего результата и снова отобразить каждый объект в результате ...

Есть ли способ сделать это как вариант 1?

Спасибо !

Редактировать 1:

По запросу Люциана Баргаоану я добавил BuildExecutionPlan:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<TblDatOperationalHUTrace, PalletResult>()
        .ForMember(dest => dest.TransCode, act => act.MapFrom((src, _, transactionCode, ctx) =>
            ctx.Mapper.Map<TblLstCode, TransactionCodeResult>(src.TransCode, 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, dest, 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.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                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.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                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.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                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.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                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.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                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.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                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.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                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.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                PropertyMap);

            return null;
        }
        afterFunction.Invoke(src, typeMapDestination, 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

1 Ответ

1 голос
/ 09 января 2020

Хорошо, я на самом деле нашел решение сам:

Я изменил 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

И тогда это работает, как и должно.

...