4

I am using the AutoMapper extensions for ASP.NET Core, so am registering Automapper at Startup like in this official example. I would like to compile mappings at startup rather letting Automapper do the default lazy compilation, but can't seem to figure out how to do eager compilation (documented here) with this setup. I tried the following, but from what I can tell it isn't causing the compilation to occur:

services.AddAutoMapper();
Mapper.Configuration.CompileMappings();

The problem I am trying to solve is that one particular mapping is rather complicated, calling multiple formatting functions, and the first hit is always very slow. I'd like to have that cost incurred when the application starts up rather than on the first map execution.

EDIT: After further investigation it seems like the performance hit comes from the EF Core compilation of the query, which lead me to try pre-compiling the query like this:

var query = EF.CompileQuery((MyProjectContext db, int id) =>
  db.Product.ProjectTo<Models.Product>().FirstOrDefault()
);
var product= query(context, 1);

However, it seems ProjectTo can't be used with EF.CompileQuery, as I get this error, when I try to execute:

Test method MyProject.Test.MappingTest.ProductGetPreCompile threw exception: 
System.NotSupportedException: Could not parse expression 'value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MyProject.Data.Entities.Product]).ProjectTo(value(System.Linq.Expressions.Expression`1[System.Func`2[MyProject.Web.Models.Product,System.Object]][]))': This overload of the method 'AutoMapper.QueryableExtensions.Extensions.ProjectTo' is currently not supported.
    at Remotion.Linq.Parsing.Structure.MethodCallExpressionParser.GetNodeType(MethodCallExpression expressionToParse)
at Remotion.Linq.Parsing.Structure.MethodCallExpressionParser.Parse(String associatedIdentifier, IExpressionNode source, IEnumerable`1 arguments, MethodCallExpression expressionToParse)
at Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseMethodCallExpression(MethodCallExpression methodCallExpression, String associatedIdentifier)
at Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseNode(Expression expression, String associatedIdentifier)
at Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseMethodCallExpression(MethodCallExpression methodCallExpression, String associatedIdentifier)
at Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseTree(Expression expressionTree)
at Remotion.Linq.Parsing.Structure.QueryParser.GetParsedQuery(Expression expressionTreeRoot)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](Expression query, INodeTypeProvider nodeTypeProvider, IDatabase database, IDiagnosticsLogger`1 logger, Type contextType)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CreateCompiledQuery[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQuery`2.CreateCompiledQuery(IQueryCompiler queryCompiler, Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryBase`2.<>c__DisplayClass6_0.<EnsureExecutor>b__0(TContext c, LambdaExpression q)
at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam1,TParam2,TValue](TValue& target, TParam1 param1, TParam2 param2, Func`3 valueFactory)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryBase`2.EnsureExecutor(TContext context)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryBase`2.ExecuteCore(TContext context, CancellationToken cancellationToken, Object[] parameters)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryBase`2.ExecuteCore(TContext context, Object[] parameters)
at MyProject.Test.MappingTest.ProductGetPreCompile() in C:\projects\MyCompany\MyCustomer\MyProject\MyProject.Test\MappingTest.cs:line 47
DanO
  • 911
  • 1
  • 8
  • 16
  • That's old. See [this](https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection). Pass assemblies or types and see how it goes. – Lucian Bargaoanu May 11 '18 at 14:42
  • I changed to services.AddAutoMapper(Assembly.GetExecutingAssembly()); but that didn't seem to make any difference. The first hit is still very slow. – DanO May 12 '18 at 01:29
  • Make a test without DI, does it work that way? – Lucian Bargaoanu May 12 '18 at 09:33
  • Good point - I created a test that does not use Dl, and am having the same experience - compiling mappings ahead of time seems to have no performance benefit. In fact, sometimes it was slightly slower with pre-compiled mappings...? This is with an EF Core entity and ProjectTo, and I am calling Mapper.Initialize and CompileMappings in the test class initialization, then checking elapsed time before and after the ProjectTo call. – DanO May 12 '18 at 18:15
  • You need to profile it and find out where that time is spent. I'm guessing is not on compiling the AM execution plan. – Lucian Bargaoanu May 12 '18 at 19:06
  • I've set up Application Insights to profile, but am not clear on how to determine the amount of time being spent on AM compilation vs EF Core query compilation. I'd like to try using EF.CompileQuery to get EF Core to compile ahead of time, but seems like that may not be supported with AM ProjectTo. – DanO May 14 '18 at 18:58
  • You don't need Application Insights. The built in VS profiler is enough. If that seems difficult, you should simply separate the parts and try to find out what takes longer. For example if you really think AM is the problem, remove everything else. – Lucian Bargaoanu May 14 '18 at 19:10
  • From what I can tell the slowness is with EF Core compiling the query (generated by ProjectTo). Do you know if there is a way to use EF.CompileQuery with ProjectTo? When I try I get "This overload of the method 'AutoMapper.QueryableExtensions.Extensions.ProjectTo' is currently not supported." – DanO May 14 '18 at 20:03
  • Post exception.ToString() – Lucian Bargaoanu May 15 '18 at 04:40
  • @LucianBargaoanu - posted the code and error as an edit as it's too long for a comment. Thanks for taking a look at it! – DanO May 15 '18 at 15:17
  • The idea is to pass the expression AM generates, not the result of the query. I'm sure people have done this before. – Lucian Bargaoanu May 15 '18 at 15:28
  • Not sure I follow - how would I pass the expression? – DanO May 15 '18 at 16:17

0 Answers0