Why is AutoMapper enumerating an IEnumerable multiple times?

Viewed 74

We ran in to performance issues and tracked it down to AutoMapper enumerating twice over an IEnumerable. It's easily fixable on our side by simply feeding AutoMapper a List or Array, but I'm still curious if this is expected behavior.

The following minimal repro test fails::

[Test]
public void AutoMapper_Should_Not_Enumerate_Multiple_Times()
{
    var counter = 0;
    var values = Enumerable.Range(0, 3).Select(_ => counter++);
    var mapper = new Mapper(new MapperConfiguration(_ => { }));

    var dest= mapper.Map<int[]>(values);

    Assert.Equal(3, counter);
}

Result: Counter = 6, dest = [3,4,5], which means the IEnumerable is iterated twice and the second iteration is returned.

I tried debugging this in AutoMapper, but I only figured out that something was first calling Count() and then iterating:

enter image description here

enter image description here

Is this expected behavior? How or where is an IEnumerable mapped to an Array and why is Count() called before iterating?

1 Answers

This is implemented by the ArrayMapper and the implementation is functionally this:

var count = source.Count();
var array = new TDestination[count];

int i = 0;
foreach (var item in source)
    array[i++] = newItemFunc(item, context);
return array;
Related