1

I want to automap from a model to a viewmodel through. My viewmodel looks like this, the model (FooBarRecord) has few extra attributes, the bar attribute is the same:

public class FooBarVm
{
    public int id { get; set; }
    public BarRecord bar { get; set; }
    public string foo { get; set; }
    public id barbaz_id { get; set; }
    public string barbaz_name { get; set; }
}

public class FooBarRecord
{
    public int id { get; set; }
    public BarRecord bar { get; set; }
    public string foo { get; set; }
    public int foo2 { get; set; }
    public BarBazRecord barbaz { get; set; }
}

public class BarRecord
{
    public int id { get; set; }
    public BarBazRecord baz { get; set; }
    public string bar1 { get; set; }
    public int bar2 { get; set; }
}

public class BarBazRecord
{
    public int id { get; set; }
    public string name { get; set; }
}

The problem is in the bar column. My automapper configuration looks like this:

Mapper.CreateMap<FooBarRecord, FooBarVm>()
    .ForMember(s => s.bar, m => m.MapFrom(rec => rec.bar))
    //other columns - those are OK
;

I also tried few other ways, like defining a mapping from BarRecord to BarRecord, or substitution of rec for rec.bar and mapping from FooBarRecord to BarRecord. The only solution I found to work is flattening the BarRecord, adding all its attributes to the FooBarVm instead of a single reference - but that's ugly and hard to maintain, definitely not what I want.

EDIT: the classes are just examples, real ones have more attributes, but I've checked they are OK. What might play some role is that among BarRecord's attributes there is one object, so I added it as BarBazRecord.

The error message is following:

The following 4 properties on Foo.Bar.ViewModels.FooBarVm are not mapped:
bar1
bar2
id
name

Those are all non-object attributes of BarRecord and BarBazRecord. I tried to resolve them like this:

.ForMember(s => s.bar.bar1, m => m.MapFrom(rec => rec.bar.bar1))

or even ignore them:

.ForMember(s => s.bar.bar1, opt => opt.Ignore())

but the error message just changed to "System.ArgumentException: Expression 's => s.poskytovatel.ulice' must resolve to top-level member." The only solution I confirmed to work is to add the whole list from the first error message (tens of attributes in my real situation) just to have to change code like from bar = vm.bar; to bar = GetBar(vm.bar_id); This is a workaround, but I would prefer to learn a proper solution.

EDIT2: even the workaround fails :-( No matter what I do, my first error is still there. I changed bar to bar_id add all the BarRecord attributes to FooBarVm. Then I tried to map the new attributes (m.MapFrom(rec => rec.bar.bar1)) or to ignore them (opt => opt.Ignore()), or even to remove any reference to BarRecord from FooBarVm and the automapper configuration. Nothing changed - all the attributes not directly present in FooBarRecord are listed as not mapped, even if ignored or mapped from nested models. Adding dummy attributes to FooBarRecord is clearly not the right way, though it's the only way that proved to affect the error message, except for overlaying it with other errors.

I updated the models - BarBazRecord is referenced from FooBarRecord as well, and FooBarVm contains attributes mapped from BarBazRecord. These attributes seem mapped properly, they are not contained in the list o unmapped properties. That's even weirder.

Community
  • 1
  • 1
Pavel V.
  • 2,653
  • 10
  • 43
  • 74
  • Post the declaration of `FooBarRecord` and `BarRecord` as well - AutoMapper should automatically map (hence the name) properties with the same name, so you shouldn't need to specify a mapping from `bar` to `bar`. – D Stanley Feb 09 '16 at 15:50
  • @DStanley: editted. Some of my steps are an expression of sheer despair, mapping between same classes is one of them. – Pavel V. Feb 10 '16 at 07:14

2 Answers2

1

I'm posting this as an answer, because it's too big for a comment. In its current form, using Automapper 4.2, your mapping should just work (see below). The only changes I've made are to define barbaz_id as an int, rather than an id in order to get the code to compile. So, for the types:

public class FooBarVm {
    public int id { get; set; }
    public BarRecord bar { get; set; }
    public string foo { get; set; }
    public int barbaz_id { get; set; }
    public string barbaz_name { get; set; }
}

public class FooBarRecord {
    public int id { get; set; }
    public BarRecord bar { get; set; }
    public string foo { get; set; }
    public int foo2 { get; set; }
    public BarBazRecord barbaz { get; set; }
}

public class BarRecord {
    public int id { get; set; }
    public BarBazRecord baz { get; set; }
    public string bar1 { get; set; }
    public int bar2 { get; set; }
}

public class BarBazRecord {
    public int id { get; set; }
    public string name { get; set; }
}

Define the mapping:

Mapper.CreateMap<FooBarRecord, FooBarVm>();

Define a source:

var src = new FooBarRecord { bar = new BarRecord { bar1 = "b1", bar2 = 23, 
                                                   baz = new BarBazRecord { id = 5, 
                                                                           name = "nme" }, 
                                                   id = 2 }, 
                             barbaz = new BarBazRecord { name = "nee", 
                                                         id = 3 }, 
                             foo = "fooo", foo2 = 123, id = 99 };

And map it to the destination:

var dest = Mapper.Map<FooBarVm>(src);

All fields are mapped, without any errors. If you're doing something different, you should update your question to make the error reproducible. Alternately, you might want to check you're using an up to date version of the mapper.

forsvarir
  • 10,749
  • 6
  • 46
  • 77
  • So you just tell me to a) use current version and b) make sure the source object is correctly set? a) is a good point, I'll try to upgrade from 3.3.1 to 4.2. I can check again about b), but it works elsewhere, so I don't suppose the problem to come from an incorrect source object. Or did you want to tell me something more that I didn't understand? – Pavel V. Feb 11 '16 at 10:37
  • @PavelV.Basically, I'm saying that your problem as posted, should work with automapper out of the box without any special configuration. That suggests that it is either a version issue, or some external factor that isn't visible in your post. It *could* be to do with other properties you've not posted, or it *could* be other conflicting mapping definitions or it *could* be the way you're invoking the mapping or ... without a reproducible problem, it's guess work. Have you actually reproduced the issue with the code you've posted and if so, was it in isolation.. – forsvarir Feb 11 '16 at 10:50
  • You were right - I commented out all other migrations and this problem disappeared. – Pavel V. Feb 11 '16 at 12:14
0

The problem was in another mapping, but I needed to solve it as well, just little later. Finally, correct use of ForAllMembers(opt => opt.Ignore() did the trick.

Mapper.CreateMap<FooBarRecord, FooBarVm>().ForAllMembers(opt => opt.Ignore());
Mapper.CreateMap<FooBarRecord, FooBarVm>()
    .ForMember(s => s.bar, m => m.MapFrom(rec => rec.bar))
    //other columns - those are OK
;
Community
  • 1
  • 1
Pavel V.
  • 2,653
  • 10
  • 43
  • 74