Skip to content

Mapster.EFCore ProjectToType<T>().FirstOrDefaultAsync throwing exception #820

@ScarletKuro

Description

@ScarletKuro

Very similar problem as here #566
But I don't think that "materializing the query" is an acceptable workaround:

This WORKS (without using IMapper)

var template = await query
	.ProjectToType<TemplateDto>()
	.FirstOrDefaultAsync();

This WORKS (with IMapper but FirstOrDefault)

var template = _mapper.From(query)
	.ProjectToType<TemplateDto>()
	.FirstOrDefault();

This doesn't WORK (With IMapper but FirstOrDefaultAsync)

var template = await _mapper.From(query)
	.ProjectToType<TemplateDto>()
	.FirstOrDefaultAsync();

And thows:

System.MissingMethodException
  HResult=0x80131513
  Message=Constructor on type 'Mapster.EFCore.MapsterAsyncEnumerable`1[[MessageDispatcher.Shared.Restful.Api.Dto.TemplateDto, MessageDispatcher.Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' not found.
  Source=System.Private.CoreLib
  StackTrace:
   at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture)
   at System.Activator.CreateInstance(Type type, Object[] args)
   at Mapster.EFCore.MapsterQueryableProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at MessageDispatcher.Services.TemplateService.<GetTemplateByNameAsync>d__7.MoveNext() in C:\Users\Dell\source\repos\MessageDispatcher\MessageDispatcher\Services\TemplateService.cs:line 64
   at MessageDispatcher.Controllers.TemplateController.<GetTemplateByName>d__3.MoveNext() in C:\Users\Dell\source\repos\MessageDispatcher\MessageDispatcher\Controllers\TemplateController.cs:line 38
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.<Execute>d__0.MoveNext()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeNextActionFilterAsync>g__Awaited|10_0>d.MoveNext()

The mapper itself

public class MapsterConfig : IRegister
{

    public void Register(TypeAdapterConfig config)
    {
        ...

        config.NewConfig<TemplateTranslationEntity, TemplateDto.TranslationDto>()
            .ConstructUsing(locale => new TemplateDto.TranslationDto(locale.Locale, locale.Text));

        config.NewConfig<TemplateEntity, TemplateDto>()
            .ConstructUsing(template => new TemplateDto(template.Name, template.Translations.Adapt<IReadOnlyList<TemplateDto.TranslationDto>>()));

		...
    }
}

public class TemplateTranslationEntity
{
    public long Id { get; set; }

    public long TemplateId { get; set; }

    public string Locale { get; set; } = null!;

    public string Text { get; set; } = null!;

    public TemplateEntity Template { get; set; } = null!;

    public LocaleEntity LocaleInfo { get; set; } = null!;
}

public class TemplateDto
{
    [JsonPropertyName("name")]
    public string Name { get; }

    [JsonPropertyName("translations")]
    public IReadOnlyList<TranslationDto> Translations { get; }

    [JsonConstructor]
    public TemplateDto(string name, IReadOnlyList<TranslationDto> translations)
    {
        Name = name;
        Translations = translations;
    }

    public class TranslationDto
    {
        [JsonPropertyName("languageCode")]
        public string LanguageCode { get; }

        [JsonPropertyName("content")]
        public string Content { get; }

        [JsonConstructor]
        public TranslationDto(string languageCode, string content)
        {
            LanguageCode = languageCode;
            Content = content;
        }
    }
}

Why does await query.ProjectToType is different from _mapper.From(query).ProjectToType?

NB! Switching from constructor to property mapping gave the same error.

Additional info:

.NET 9.0
Mapster 7.4.0
Mapster.EFCore 5.1.1
Mapster.DependencyInjection 1.0.1

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions