Repository navigation
Mapster.EFCore ProjectToType<T>().FirstOrDefaultAsync throwing exception #820
Copy link
Copy link
Closed
Labels
Description
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
Reactions are currently unavailable
Activity
ScarletKuro commentedon Oct 13, 2025
I don't think it should even wrap in
MapsterAsyncEnumerableforFirstOrDefaultAsynchere?Shouldn't it be something like
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default) { var enumerable = ((IAsyncQueryProvider)_provider).ExecuteAsync<TResult>(expression, cancellationToken); var enumerableType = typeof(TResult); + if (!IsAsyncEnumerableType(enumerableType)) + { + return enumerable; + } var elementType = enumerableType.GetGenericArguments()[0]; ... } +private static bool IsAsyncEnumerableType(Type type) +{ + return type.GetInterfaces() + .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>)); +}in
MapsterQueryable.csAt least it fixes the problem, but I didn't test it properly yet.
DocSvartz commentedon Oct 13, 2025
@ScarletKuro Yes, The problem here is that FirstOrDefaultAsync() doesn't return an Enumerator, it return Task
But for some reason, it's handled by the Enumerator code.
ScarletKuro commentedon Oct 14, 2025
Made a PR #821 with a test. Hopefully this can be merged soon and released so I don't need to drag a custom version.
DocSvartz commentedon Oct 14, 2025
@ScarletKuro Great job! 👍
ScarletKuro commentedon Mar 6, 2026
Guess this can be closed if #823 was merged in the main?
DocSvartz commentedon Mar 6, 2026
@ScarletKuro Yes, it is will be merged.