Base solution for your next web application
Open Closed

How to automap a joined entity by ObjectMapper? #4149


User avatar
0
fguo created

I need to output a list (EntityListDto) to client. This EntityListDto is mapped from two entities:

[AutoMapFrom(typeof(Entity1), typeof(Entity2))] public class EntityListDto : EntityDto { public string PropertyFromEntity1 { get; set; } public string PropertyFromEntity2 { get; set; } }

I made a service:

    public EntityListDto GetEntity(int Id)
    {
        var entity1 = _entityt1Repository
            .GetAll()
            .Where(w => w.Id == Id);

        var entity2 = _entity2Repository
            .GetAll()
            .Where(w => w.FKId == Id);

        var entity = entity1.GroupJoin(entity2, e1 => e1.Id, e2 => e2.FkId, (e1, e2) => new { entity1 = e1, entity2 = e2.FirstOrDefault() }).FirstOrDefault();

        return ObjectMapper.Map<EntityListDto>(entity);
    }

It throws an exception on the last line:

AutoMapper.AutoMapperMappingException: 'Missing type map configuration or unsupported mapping.'

How should I use ObjectMapper to Map the joined entity?

Thanks,


4 Answer(s)
  • User Avatar
    0
    aaron created
    Support Team

    Your entity is anonymous type since you GroupJoin with new, so the mapping is unsupported.

    Do this:

    public EntityListDto GetEntity(int Id)
    {
        var entity1 = _entity1Repository.Get(Id);
    
        var entity2 = _entity2Repository.FirstOrDefault(w => w.FKId == Id);
    
        var dto = ObjectMapper.Map<EntityListDto>(entity1);
    
        return ObjectMapper.Map(entity2, dto);
    }
    
  • User Avatar
    0
    fguo created

    Nice and Neat! Thank you very much!!

    However, I got two glitches:

    I actually want to entity1 LEFT JOIN entity2. Your code seems just working as "inner Join". When I try your code with an empty entity2, it results an empty EntityDto. It should be an EntityDto with values of all PropertyFromEntity1 but null for all PropertyFromEntity2. I reversed the order of mapping and solved it. Is it the correct way?

    Another problem is about Id. As convention, both entity1 and entity2 have field "Id". I want to keep entity1.Id as EntityDto.Id and ignore entity2.Id. When I try your code, it returns the EntityDto.Id = entity2.Id, because the last mapping is on the entity2, I think. I surely can reverse the order of mapping in this particular example to solve this issue, but generally, how do I handle if entity1 and entity2 have same name properties?

    For example, if I need:

    [AutoMapFrom(typeof(Entity1), typeof(Entity2))] public class EntityListDto : EntityDto { public string XYZ { get; set; } // map with Entity1.XYZ, instead of Entity2.XYZ public string ABC { get; set; } // map with Entity2.ABC, instead of Entity1.ABC }

    Usually, most of properties can be auto-mapped correctly, but only few of them (such as Id, name, email ...) have this kind of issue when I map joined entities. Can you show me a correct way?

    Thanks again!

  • User Avatar
    0
    aaron created
    Support Team

    When I try your code with an empty entity2, it results an empty EntityDto. It should be an EntityDto with values of all PropertyFromEntity1 but null for all PropertyFromEntity2. I reversed the order of mapping and solved it. Is it the correct way?

    Yes, that's AutoMapper behavior for null source. Alternatively, you can add a null-check.

    I want to keep entity1.Id as EntityDto.Id and ignore entity2.Id. When I try your code, it returns the EntityDto.Id = entity2.Id, because the last mapping is on the entity2, I think. I surely can reverse the order of mapping in this particular example to solve this issue...

    Yes, map the overriding one last.

    ...but generally, how do I handle if entity1 and entity2 have same name properties?

    Create a Profile:

    public class EntityListDtoProfile : Profile
    {
        public EntityListDtoProfile()
        {
            CreateMap<Entity1, EntityListDto>()
                .ForMember(dest => dest.ABC, opt => opt.Ignore()); // ignore Entity1.ABC
    
            CreateMap<Entity2, EntityListDto>()
                .ForMember(dest => dest.XYZ, opt => opt.Ignore()); // ignore Entity2.XYZ
        }
    }
    

    Add profiles by assembly scanning in the PreInitialize method of YourApplicationModule:

    Configuration.Modules.AbpAutoMapper().Configurators.Add(cfg =>
    {
        cfg.AddProfiles(typeof(YourApplicationModule).GetAssembly());
    });;
    
  • User Avatar
    0
    fguo created

    Excellent!!

    I will try this way later when I handle complicated mapping case.

    Thanks again!!