0

I have a very wierd error that I can't get my head around. I'm using AutoMapper 6 with AutoMapper.Collection and AutoMapper.Collection.EntityFramework.

https://github.com/AutoMapper/AutoMapper.Collection

As you can see from the screenshot below, every component is updated apart from Image that is null for updatedContact. If I however do an explicit mapping for only updatedImage it works. It also works to update a collection of images without a problem. Has anyone experienced this? Other single properties works as well but for some reason Image is causing trouble.

//Works
var updatedArticle = Mapper.Map<ArticleViewModel, Article>(articleVm, articleOriginal);
//Every component is updated a part from Image.
var updatedContact = Mapper.Map<ContactViewModel, Contact>(contactVm, contactOriginal);
//Works
var updatedImage = Mapper.Map<ImageViewModel, Image>(contactVm.Image);
//Works
var newContact = Mapper.Map<ContactViewModel, Contact>(contactVm);

enter image description here

Mapping:

cfg.CreateMap<ArticleViewModel, Article>(MemberList.Source)
    .EqualityComparison((src, dst) => src.Id == dst.Id);

cfg.CreateMap<ImageViewModel, Image>(MemberList.Source)
    .EqualityComparison((src, dst) => src.Id == dst.Id)
    .ForSourceMember(x => x.IsDeleted, opt => opt.Ignore())
    .ForMember(dest => dest.ImageBytes, opt => opt.MapFrom(src => Encoding.ASCII.GetBytes(src.Image)));

cfg.CreateMap<ContactViewModel, Contact>(MemberList.Source)
.EqualityComparison((src, dst) => src.Id == dst.Id)
.ForSourceMember(x => x.IsDeleted, opt => opt.Ignore())
.ForSourceMember(x => x.FullName, opt => opt.Ignore());

Files:

public class ArticleViewModel
{
    public int Id { get; set; }
    ... 
    public List<ImageViewModel> Images { get; set; }
}

public class Article : IEntity<int>
{
    public int Id { get; set; }
    ...
    public virtual ICollection<Image> Images { get; set; }  
}

public class ContactViewModel
{
    public int Id { get; set; }
    ... 
    public ImageViewModel Image { get; set; }
}

public class Contact: IEntity<int>
{
    [Key]
    public int Id { get; set; }

    ...

    public int? ImageId { get; set; }

    public Image Image { get; set; }

}

public class ImageViewModel
{
    public int Id { get; set; }

    public string Image { get; set; }

    public string ImageType { get; set; }

    public bool IsDeleted { get; set; }
}

public class Image : IEntity<int>
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public DateTime Created { get; set; }

    public DateTime Updated { get; set; }

    public byte[] ImageBytes { get; set; }

    public string ImageType { get; set; }

    public int? ArticleId { get; set; }

    public virtual Article Article { get; set; }
}
Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • 1
    *“Other single properties works as well but for some reason Image is causing trouble.”* – And yet you say in your code snippet that mapping `Image` works and `Contact` does not work. Can you please provide a [mcve] that actually shows the problem? – poke Jul 20 '17 at 08:06
  • @poke Badly formulated, instead of does not work it should be `Every component is updated a part from Image` as you can see in the screenshot. – Ogglas Jul 20 '17 at 08:09
  • Well I haven’t seen any proof that *any* nested complex property gets actually mapped. `Article` in `updatedImage` is null too. – poke Jul 20 '17 at 08:11
  • The `Image` class can be used by both an `Article` and a `Contact`. Since this is a `Contact` the `Article` should be null. `Article` has a collection of images while `Contact` only has one. – Ogglas Jul 20 '17 at 08:14
  • @poke Thanks for the help, if I marked `Image` as `virtual` in `Contact` everything started working. – Ogglas Jul 20 '17 at 09:01

2 Answers2

1

Finally solved it, I had forgot to mark Image as virtual in Contact. After doing that everything started working out of the box.

public virtual Image Image { get; set; }
Ogglas
  • 62,132
  • 37
  • 328
  • 418
0

I think you need to tell your contact mapper in the config to explicitly use the mapping for the image vm. There might be one or two typos as I'm doing this from memory but it should be similar to:

.ForMember(x => x.Image, opt => opt.MapFrom(contact => Mapper.Map<ImageViewModel, Image>(contact.ImageVm);))
Adam Diament
  • 4,290
  • 3
  • 34
  • 55
  • [This article](https://github.com/AutoMapper/AutoMapper/wiki/Nested-mappings) suggests that you do not need to specify that explicitly. – poke Jul 20 '17 at 08:26
  • I do not have to do this mapping anywhere else. I think it has something to do with that I'm updating a current object and at the current object the `Image` is null. I made a new check and created a new object and then the `Image` was created. `var newContact = Mapper.Map(contactVm);` @poke – Ogglas Jul 20 '17 at 08:28
  • Fair enough. If the image is null then that explains it - what does it have to map from? If I understand you correctly make sure the image is loaded before you do the mapping? – Adam Diament Jul 20 '17 at 08:31
  • @AdamDiament `Image` has values in `contactVm` but `null` for `contactOriginal` in my example above. What I wan't then is for `updatedContact` to have the values from `contactVm`. Every other property is updated a part from `Image`. – Ogglas Jul 20 '17 at 08:35
  • Gotcha. How are you retrieving the data from the db? This sort of smells like it could be an entity-framework-trying-to-be-clever problem. If it is EF, have you tried using .Include to force it to load the image? And does it work if after you load the contactOriginal but before doing the mapping you do something like var x = contactOriginal.Image, which could force it into memory (I know not great code, just trying to diagnose); – Adam Diament Jul 20 '17 at 08:40
  • 1
    @AdamDiament Thanks for the help, if I marked `Image` as `virtual` in `Contact` everything started working. – Ogglas Jul 20 '17 at 09:02
  • Ah yes that would do it. I missed that. Cheers! – Adam Diament Jul 20 '17 at 09:04