InvalidOperationException when using DI in collection element mapping
See original GitHub issueI’ve ran into this issue last week and spent quite some time looking into it, I would love to get some explanation about the implementation chosen and what the implications are for the workaround.
Background
Our existing DTOs use IEnumerable<T> to represent child collection properties.
Some of the DTOs are complex and their child properties are of type of yet another DTO.
We define custom conversions of each POCO to DTO.
Certain POCO properties are mapped using a service injected with MapContext.
Issue
When the destination type is evaluated, the value is set to SelectListIterator and no instantiated until after the IoC container scope is disposed and therefore generating the following exception:
System.InvalidOperationException: Mapping must be called using ServiceAdapter
at Mapster.TypeAdapterExtensions.GetService[TService](MapContext context) in D:\Dev\Repos\GitHub\Mapster\src\Mapster.DependencyInjection\TypeAdapterExtensions.cs:line 19
at lambda_method(Closure , Poco )
at System.Linq.Enumerable.SelectListIterator`2.MoveNext()
...
If we change the destination type properties to use List<T> or array, it works as intended.
But we are talking about hundreds of occurrences that will need to be modified, so we should avoid that at all cost. Especially since AutoMapper works great with this scenario.
Steps to Reproduce
This is the simplified scenario written as test within Mapster.DependencyInjection.Tests.InjectionTest:
(in the test, I’m mapping directly using .Map<IEnumerable<Dto>>(... so please don’t suggest to change it to List<Dto>)
[TestMethod]
public void InjectionInsideCollection()
{
var config = new TypeAdapterConfig();
config.NewConfig<Poco, Dto>()
.Map(dest => dest.Name, _ => MapContext.Current.GetService<IMockService>().GetName());
IServiceCollection sc = new ServiceCollection();
sc.AddScoped<IMockService, MockService>();
sc.AddSingleton(config);
sc.AddScoped<IMapper, ServiceMapper>();
var sp = sc.BuildServiceProvider();
IEnumerable<Dto> dtos;
using (var scope = sp.CreateScope())
{
var mapper = scope.ServiceProvider.GetService<IMapper>();
var pocos = new List<Poco> {new Poco {Id = "bar"}};
dtos = mapper.Map<IEnumerable<Dto>>(pocos);
}
dtos.ShouldNotBeEmpty();
dtos.Count().ShouldBe(1);
dtos.Single().Name.ShouldBe("foo");
}
Workaround
I categorize it as a workaround since I’m not sure what the implications are and if none, I think that should be the default behavior. We added the following default configuration:
config.Default.Settings.AvoidInlineMapping = true;
Any help, clarification, suggestion is appreciated…
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (2 by maintainers)

Top Related StackOverflow Question
Too bad, it is because of
IEnumerable<>behavior. Enumerable computed lazily outside service provider scope.You have 2 choices.
List<>dtos.Count(),dtos.Single()inside service provider scope.This looks like it is similar to #230 I wrote a little solution/workaround on StackOverflow with an explanation on why it works this way: https://stackoverflow.com/questions/69749350/mapster-context-is-null-in-nested-mappings/75249680#75249680