在前兩天遇到 .NET Core 中 EF Core 的異步與同步查詢的百倍性能之差(詳情之前的博文)之后,這兩天又遇到了 AutoMapper ProjectTo<T>
與 Mapster ProjectToType<T>
的千倍性能之差。
問題是在昨天發現的,使用 AutoMapper ProjectTo<T> + EF Core
從數據庫中獲取20條記錄竟然耗時 10s
左右。
[Information] Executed DbCommand ("9,947"ms) [Parameters=["@__p_3='20'"], CommandType='Text', CommandTimeout='30']"
如果改為獲取10條記錄,耗時需要 5s
左右,從中可以推測某個原因造成獲取單條記錄增加了額外的開銷。
而用同樣的 SQL 語句在 SSMS 中查詢數據庫飛快,可以排除不是數據庫層面的問題。
根據前車之鑒,將異步的 ToListAsync()
改為同步的 ToList()
,但問題依舊。
對於這個奇怪的問題,從 EF Core 層面實在找不到線索,於是將懷疑的目光轉向了 AutoMapper
。
代碼中用到了 AutoMapper 的 ProjectTo
:
return await _postQueryRepository
.GetPostsByStartId(startId)
.OrderBy(p => p.Id)
.Take(itemCount)
.ProjectTo<T>()
.ToListAsync();
在配置映射時使用了字符串連接:
conf.CreateMap<BlogPost, BlogPostDto>()
.ForMember(dto => dto.AuthorUrl, opt => opt.MapFrom(p => "https://www.cnblogs.com/" + p.BlogSite.Application + "/"));
去掉上面的映射配置之后,速度立馬變得飛快,而 Executed DbCommand
執行時間從 9,947ms
飛流直下 2ms
,原來是 AutoMapper 惹的禍!
[Information] Executed DbCommand ("2"ms) [Parameters=["@__p_3='20'"], CommandType='Text', CommandTimeout='30']"
而同樣的代碼在 .NET Framework 中沒這個問題,看來是 AutoMapper
與 EF Core
相處不融洽。
原因已經找到,那如何解決或避開這個問題呢?這時想到了剛認識不久了解不多的新朋友 —— Mapster ,換上對象映射界的新秀 Mapster
試試。
將 ProjectTo
改為 ProjectToType
,並如下配置映射關系:
TypeAdapterConfig<BlogPost, BlogPostDto>.ForType()
.Map(dest => dest.AuthorUrl, src => "https://www.cnblogs.com/" + src.BlogSite.Application + "/");
運行后驚喜地發現 Mapster
沒這個問題,多次運行 Executed DbCommand
都在 10ms
以內,千倍之差,讓人眼前一亮的新秀。
[Information] Executed DbCommand ("2"ms) [Parameters=["@__p_3='20'"], CommandType='Text', CommandTimeout='30']"
在這冬去春來之際,由於遇到這個問題,也到了我們辭去 AutoMapper
迎來 Mapster
的時候。