MapsterMapper / Mapster

A fast, fun and stimulating object to object Mapper
MIT License
4.23k stars 325 forks source link

Mapping must be called using ServiceAdapter #704

Open benjaminoerskov opened 2 months ago

benjaminoerskov commented 2 months ago

Im getting the above error. I cant quite figure out what its supposed to mean.? Looking at this test, I assume that its an old errormessage, and its supposed to be ServiceMapper instead of ServiceAdapter? However, I am already using the ServiceMapper like this:

services.AddSingleton<IMapper, ServiceMapper>();

I setup my mapping configuration like this:

TypeAdapterConfig<Course, InProgressCourseViewDTO>.NewConfig()
    .AfterMappingAsync(async (course, viewDto) =>
    {
        await Helpers.SetImageUrls(course, viewDto);
    });

public static async Task SetImageUrls(Course course, InProgressCourseViewDTO dto)
{
            var blobClient = MapContext.Current.GetService<IBlobClient>();
            var blobEntry = await blobClient.GetPublicBlobAsync(course.blobReference.EntryId);
            dto.Url = blobEntry?.Url ?? default;
}

and using it like this:

private async Task<InProgressCourseViewDTO> MapInProgress(Course course)
{
    var view = await course.BuildAdapter().AdaptToTypeAsync<InProgressCourseViewDTO>();
    return view;
}
Skyppid commented 2 months ago

Stumbled across this issue myself. Using BuildAdapter() does not use the injected service IMapper. If you need to access services, you always need to use IMapper.

Inject IMapper in your class or pass it as parameter, then you can use await mapper.From(source).AdaptToTypeAsync<Destination>() and it will work. Only then the MapContext has access to the services.

benjaminoerskov commented 2 months ago

Thanks @Skyppid! It seems like this is still an issue though, as the docs describe that BuildAdapter() should work equally well.

Skyppid commented 2 months ago

Thanks @Skyppid! It seems like this is still an issue though, as the docs describe that BuildAdapter() should work equally well.

My assumption is since Mapster is not exclusively for ASP.Net that the docs relate to the general usage. Accessing DI in the async after-delegate is very specific to ASP.Net though and in this case you're right. The docs should be extended to supply that information for this context.

But it only makes sense that in a general scenario where you don't have DI you'd approach it like it is stated in the docs. Took me a while to figure out as well, but I guess this was never considered during the development of Async. In general the Async extension is quite a whacky workaround. It works but I'd say it's not a beautiful implementation.

At least I'm having serious issues with it when it comes to polymorphic DTOs...