Madd0g
Madd0g

Reputation: 4001

AutoMapper - mapping child collections in viewmodel

I have a viewmodel that needs to display a certain IEnumerable field as semicolon-separated textbox. At first I thought of using DefaultModelBinder to transform it, but I had trouble thinking how to achieve it in both directions (dto <-> viewmodel).

Nicknames is the field I'm trying to display as one textbox separated by semicolon.

public class Parent
{
    public IEnumerable<Child> Children { get; set; }
}

public class Child
{
    public IEnumerable<string> Nicknames { get; set; }
}

So I decided to try AutoMapper, I created two ViewModels:

public class ParentViewModel
{
    public IEnumerable<ChildViewModel> Children { get; set; }
}

public class ChildViewModel
{
    public string Nicknames { get; set; }
}

Then, I created mappings, like this for the children (omitted the other-way conversion for brevity)

Mapper.CreateMap<Child, ChildViewModel>().ForMember(
d => d.Nicknames, o => o.ResolveUsing<ListToStringConverter>().FromMember(s => s.Nicknames);

Then, for the parent, created a naive map (again, omitted the other-way)

Mapper.CreateMap<Parent, ParentViewModel>();

I truly expected the child mappings occur automatically, but they don't, I've already created too much "proper" code to solve a really simple problem which in any other simpler/older non-MVC environment, I'd be done with a long time ago :) How can I proceed and tell AutoMapper to transform the children without writing another "children member resolver".

Have I overthought this and there's a simpler way?

Thank you!

Upvotes: 12

Views: 16991

Answers (2)

OzBob
OzBob

Reputation: 4520

Found this solution https://stackoverflow.com/a/7555977/1586498, that works for me:

Mapper.CreateMap<ParentDto, Parent>()
  .ForMember(m => m.Children, o => o.Ignore()) // To avoid automapping attempt
  .AfterMap((p,o) => { o.Children = ToISet<ChildDto, Child>(p.Children); });

The ToISet function is defined in the above link.

Simpler examples 'just work' in LinqPad - so more investigation is required.

A complete listing of a working program:

    public class Child{ public string Name  {get; set; }}
    public class ChildDto{  public string NickName {get; set; }}
    public class Parent{    public virtual IEnumerable<Child> Children  {get; set; }}
    public class ParentDto{ public IEnumerable<ChildDto> Kids  {get; set; }}

    private static void Main()
    {
        AutoMapper.Mapper.CreateMap<Parent, ParentDto>().ForMember(d=>d.Kids, opt=>opt.MapFrom(src=>src.Children));
        AutoMapper.Mapper.CreateMap<Child, ChildDto>().ForMember(d=>d.NickName, opt=>opt.MapFrom(src=>src.Name));

        var pList = new HashSet<Parent>{
            new Parent{ Children = new HashSet<Child>{new Child{Name="1"}, new Child{Name="2"}}},
            new Parent{ Children = new HashSet<Child>{new Child{Name="3"}, new Child{Name="4"}}},
        };

        var parentVm = AutoMapper.Mapper.Map<IEnumerable<Parent>, IEnumerable<ParentDto>>(pList);
        parentVm.Dump();    
    }

Upvotes: 6

Rafay
Rafay

Reputation: 31033

try

Mapper.CreateMap<Parent, ParentViewModel>();
Mapper.CreateMap<Child, ChildViewModel>();

var v = Mapper.Map<Parent, ParentViewModel>(parent);

Upvotes: 14

Related Questions