1
votes

I have 2 classes, Class1 should be mapped to Class2. I do mapping with AutoMapper. I'd like to test my configuration of the mapper and for this purposes I'm using AutoFixture. Source class Class1 has property of type IList<>, destination class Class2 has a similar property but of type IEnumerable<>. To simplify test preparation I'm using AutoFixture (with AutoMoqCustomization) to initialize both source and destination objects. But after initializing property of type IEnumerable<> with AutoFixture, AutoMapper can't map the property.

Error text:

Error mapping types.

Mapping types: Class1 -> Class2 ConsoleApplication1.Class1 -> ConsoleApplication1.Class2

Type Map configuration: Class1 -> Class2 ConsoleApplication1.Class1 -> ConsoleApplication1.Class2

Property: Items

Could anybody help me to configure either AutoMapper or AutoFixture to make the mapping work? As a workaround I can assign null to the destination property, but I do not want to do this in the each test.

Simplified example of code:

public class AutoMapperTests
{
    public static void TestCollectionsProperty()
    {
        Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<ItemClass1, ItemClass2>();
            cfg.CreateMap<Class1, Class2>();
        });

        var src = new Class1();
        src.Items = new List<ItemClass1>()
        {
            new ItemClass1() { Text = "111" },
            new ItemClass1() { Text = "222" }
        };

        var fixture = new Fixture();
        var dst = fixture.Create<Class2>();

        Mapper.Map(src, dst); //Error at this line of code
    }
}

public class Class1
{
    public IList<ItemClass1> Items { get; set; }
}

public class Class2
{
    public IEnumerable<ItemClass2> Items { get; set; }
}

public class ItemClass1
{
    public string Text { get; set; }
}
public class ItemClass2
{
    public string Text { get; set; }
}
3
Code as posted doesn't compile, as (AFICT) Mapper.CreateMap has been deprecated and removed from AutoMapper. Please update repro code, or post a list of NuGet packages in use, including their version numbers. - Mark Seemann
Thank you for comment, I've updated the example - Alexander Lysenko

3 Answers

1
votes

It's not really an AutoFixture issue per se. You can reproduce it without AutoFixture by instead creating dst like this:

var dst = new Class2();
dst.Items = Enumerable.Range(0, 1).Select(_ => new ItemClass2());

This will produce a similar error message:

Unable to cast object of type 'WhereSelectEnumerableIterator2[System.Int32,Ploeh.StackOverflow.Q45437098.ItemClass2]' to type 'System.Collections.Generic.IList1[Ploeh.StackOverflow.Q45437098.ItemClass2]'

That ought to be fairly self-explanatory: WhereSelectEnumerableIterator<int, ItemClass2> doesn't implement IList<ItemClass2>. AutoMapper attempts to make that cast, and fails.

The simplest fix is probably to avoid populating dst:

var dst = new Class2();

If you must use AutoFixture for this, you can do it like this:

var dst = fixture.Build<Class2>().OmitAutoProperties().Create();

Unless the Class2 constructor does something complex, however, I don't see the point of using AutoFixture in that scenario.

If, on the other hand, you do need dst to be populated, you just need to ensure that dst.Items is convertible to IList<ItemClass2>. One way to do that would be like this:

var dst = fixture.Create<Class2>();
dst.Items = dst.Items.ToList();

You could create a Customization to make sure that this happens automatically, but if you need help with that, please ask a new question (if you don't find one that already answers that question).

0
votes

Here is a working example for your problem. As @Mark Seemann already told, Mapper.CreateMap has been deprecated, so this example is using the new structure.

Mapper.Initialize(cfg =>
{
    cfg.CreateMap<ItemClass1, ItemClass2>();
    cfg.CreateMap<Class1, Class2>();
});

var src = new Class1();
src.Items = new List<ItemClass1>()
{
    new ItemClass1() { Text = "111" },
    new ItemClass1() { Text = "222" }
};

var dest = Mapper.Map<Class1, Class2>(src);
0
votes

AM requires IList because you're mapping to an existing list and that works by calling IList.Add.